eip2nats 1.0.0__tar.gz

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.
@@ -0,0 +1,67 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ pip-wheel-metadata/
20
+ share/python-wheels/
21
+ *.egg-info/
22
+ .installed.cfg
23
+ *.egg
24
+ MANIFEST
25
+
26
+ # Virtual Environment
27
+ venv/
28
+ env/
29
+ ENV/
30
+ .venv/
31
+
32
+ # PyPI
33
+ .pypirc
34
+
35
+ # Hatch
36
+ .hatch/
37
+ .env
38
+
39
+ # Testing
40
+ .pytest_cache/
41
+ .coverage
42
+ htmlcov/
43
+ .tox/
44
+ .hypothesis/
45
+
46
+ # IDEs
47
+ .vscode/
48
+ .idea/
49
+ *.swp
50
+ *.swo
51
+ *~
52
+ .DS_Store
53
+
54
+ # Project specific
55
+ # Mantener la estructura pero ignorar archivos compilados
56
+ src/eip2nats/lib/*.so*
57
+ src/eip2nats/lib/*.a
58
+
59
+ # Build artifacts
60
+ build/dependencies/
61
+ *.cmake
62
+ CMakeCache.txt
63
+ CMakeFiles/
64
+ Makefile
65
+
66
+ # Logs
67
+ *.log
eip2nats-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Your Name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,333 @@
1
+ Metadata-Version: 2.4
2
+ Name: eip2nats
3
+ Version: 1.0.0
4
+ Summary: EtherNet/IP to NATS Bridge with bundled dependencies
5
+ Project-URL: Homepage, https://github.com/yourusername/eip2nats
6
+ Project-URL: Documentation, https://github.com/yourusername/eip2nats/blob/main/README.md
7
+ Project-URL: Repository, https://github.com/yourusername/eip2nats
8
+ Author-email: Your Name <your.email@example.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: C++
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.7
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Requires-Python: >=3.7
23
+ Requires-Dist: pybind11>=2.6.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: black>=22.0; extra == 'dev'
26
+ Requires-Dist: pytest>=7.0; extra == 'dev'
27
+ Requires-Dist: ruff>=0.0.243; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # eip2nats - EtherNet/IP to NATS Bridge
31
+
32
+ Puente completo entre dispositivos EtherNet/IP (PLCs) y servidores NATS, **con todas las dependencias incluidas** en el wheel.
33
+
34
+ ## ✨ Características
35
+
36
+ - ✅ **Self-contained**: Incluye libnats y libEIPScanner compiladas
37
+ - ✅ **Zero dependencies**: No requiere instalación de librerías del sistema
38
+ - ✅ **Entorno virtual**: Compatible con Raspberry Pi OS (sin pip install global)
39
+ - ✅ **Simple instalación**: Setup automático con un comando
40
+ - ✅ **Alto rendimiento**: Bindings nativos C++ con pybind11
41
+ - ✅ **Thread-safe**: Manejo seguro de múltiples conexiones
42
+
43
+ ## 🚀 Instalación Rápida
44
+
45
+ ### Setup Completo Automático
46
+
47
+ ```bash
48
+ ./setup_project.sh
49
+ ```
50
+
51
+ Esto hace TODO automáticamente:
52
+ 1. Crea un entorno virtual en `venv/`
53
+ 2. Instala Hatch y pybind11
54
+ 3. Compila nats.c, EIPScanner y el binding Python
55
+ 4. Crea el wheel
56
+ 5. Instala el wheel en el venv
57
+
58
+ ### Uso Posterior
59
+
60
+ ```bash
61
+ # Activar entorno virtual
62
+ source venv/bin/activate
63
+
64
+ # Ejecutar ejemplo básico
65
+ python examples/basic_example.py
66
+
67
+ # O test completo
68
+ python examples/test_bridge.py
69
+
70
+ # O usar el script helper
71
+ ./run.sh # Ejecuta basic_example.py por defecto
72
+ ./run.sh python examples/test_bridge.py
73
+
74
+ # Desactivar cuando termines
75
+ deactivate
76
+ ```
77
+
78
+ ## 💻 Uso
79
+
80
+ ```python
81
+ import eip2nats
82
+ import time
83
+
84
+ # Crear el bridge
85
+ bridge = eip2nats.EIPtoNATSBridge(
86
+ "192.168.17.200", # IP del PLC
87
+ "nats://192.168.17.138:4222", # Servidor NATS
88
+ "plc.data" # Subject/topic NATS
89
+ )
90
+
91
+ # Iniciar
92
+ if bridge.start():
93
+ print("✅ Bridge corriendo!")
94
+
95
+ # Monitorear
96
+ while bridge.is_running():
97
+ time.sleep(5)
98
+ print(f"📊 RX={bridge.get_received_count()}, "
99
+ f"TX={bridge.get_published_count()}")
100
+
101
+ # Detener
102
+ bridge.stop()
103
+ ```
104
+
105
+ **Ver más ejemplos en [`examples/`](examples/README.md)**
106
+
107
+ ## 📋 Requisitos
108
+
109
+ **Sistema:**
110
+ - Linux (ARM64/x86_64)
111
+ - Python 3.7+
112
+ - git, cmake, make, g++, python3-venv (solo para compilar)
113
+
114
+ **Para desarrollo:** Ejecutar `./setup_project.sh` (crea venv automáticamente)
115
+
116
+ ## 🛠️ Desarrollo
117
+
118
+ ### Modificar Código C++
119
+
120
+ Para desarrollo iterativo sin regenerar el wheel:
121
+
122
+ ```bash
123
+ # 1. Editar código
124
+ nano src/eip2nats/EIPtoNATSBridge.cpp
125
+
126
+ # 2. Opción A: Test C++ standalone (recomendado para debugging)
127
+ ./scripts/build_standalone.sh
128
+ ./test_standalone
129
+
130
+ # 3. Opción B: Compilar binding Python (test de integración)
131
+ ./scripts/build_python_binding.sh
132
+ python examples/basic_example.py
133
+ ```
134
+
135
+ **Ver guía completa:** [`DEVELOPMENT.md`](DEVELOPMENT.md)
136
+
137
+ **Incluye:**
138
+ - Workflow de desarrollo iterativo
139
+ - Debugging con VSCode (recomendado) y GDB
140
+ - Detección de memory leaks con Valgrind
141
+ - Testing C++ standalone vs Python binding
142
+ - Cuándo usar cada enfoque
143
+
144
+ ### Crear Release
145
+
146
+ ```bash
147
+ # Cuando estés satisfecho con los cambios
148
+ hatch build
149
+ ```
150
+
151
+ ### Clonar el repositorio
152
+
153
+ ```bash
154
+ git clone https://github.com/yourusername/eip2nats.git
155
+ cd eip2nats
156
+ ```
157
+
158
+ ### Instalar Hatch
159
+
160
+ ```bash
161
+ pip install hatch
162
+ ```
163
+
164
+ ### Compilar dependencias
165
+
166
+ ```bash
167
+ # Esto descarga y compila nats.c, EIPScanner y el binding Python
168
+ python scripts/build_dependencies.py
169
+ ```
170
+
171
+ O usando Hatch:
172
+
173
+ ```bash
174
+ hatch run build-deps
175
+ ```
176
+
177
+ ### Crear el wheel
178
+
179
+ ```bash
180
+ hatch build
181
+ ```
182
+
183
+ Esto genera:
184
+ - `dist/eip2nats-1.0.0-*.whl` - Wheel con todas las dependencias incluidas
185
+ - `dist/eip2nats-1.0.0.tar.gz` - Source distribution
186
+
187
+ ### Ejecutar tests
188
+
189
+ ```bash
190
+ hatch run test
191
+ ```
192
+
193
+ ## 📦 Estructura del Proyecto
194
+
195
+ ```
196
+ eip2nats/
197
+ ├── pyproject.toml # Configuración Hatch
198
+ ├── README.md
199
+ ├── src/
200
+ │ └── eip2nats/
201
+ │ ├── __init__.py # Package Python
202
+ │ ├── bindings.cpp # Bindings pybind11
203
+ │ ├── EIPtoNATSBridge.h # Header C++
204
+ │ ├── EIPtoNATSBridge.cpp # Implementación C++
205
+ │ └── lib/ # Librerías compiladas (auto-generado)
206
+ │ ├── libnats.so
207
+ │ ├── libEIPScanner.so
208
+ │ └── eip2nats.*.so
209
+ ├── scripts/
210
+ │ └── build_dependencies.py # Script de compilación
211
+ ├── tests/
212
+ │ └── test_basic.py
213
+ └── build/
214
+ └── dependencies/ # Clones de nats.c y EIPScanner
215
+ ├── nats.c/
216
+ └── EIPScanner/
217
+ ```
218
+
219
+ ## 🔧 Cómo Funciona
220
+
221
+ 1. **`scripts/build_dependencies.py`**:
222
+ - Clona nats.c desde GitHub
223
+ - Compila nats.c → `libnats.so`
224
+ - Clona EIPScanner desde GitHub
225
+ - Compila EIPScanner → `libEIPScanner.so`
226
+ - Compila el binding Python → `eip2nats.*.so`
227
+ - Copia todos los `.so` a `src/eip2nats/lib/`
228
+
229
+ 2. **`hatch build`**:
230
+ - Ejecuta el build script automáticamente
231
+ - Empaqueta `src/eip2nats/` completo (código + `.so`)
232
+ - Crea el wheel con RPATH relativo (`$ORIGIN`)
233
+ - El wheel contiene todo lo necesario
234
+
235
+ 3. **`pip install`**:
236
+ - Instala el wheel
237
+ - Los `.so` quedan en el site-packages
238
+ - Python carga las librerías automáticamente
239
+ - ¡Funciona sin dependencias del sistema!
240
+
241
+ ## 🎯 Ventajas de Este Enfoque
242
+
243
+ ### ✅ Comparado con librerías del sistema:
244
+ - No requiere `sudo apt-get install`
245
+ - No hay conflictos de versiones
246
+ - Portabilidad entre sistemas
247
+
248
+ ### ✅ Comparado con wheels normales:
249
+ - Incluye todas las dependencias C/C++
250
+ - Un solo archivo para instalar
251
+ - Funciona en sistemas sin compiladores
252
+
253
+ ### ✅ Comparado con Docker:
254
+ - Más ligero (MBs vs GBs)
255
+ - Integración directa con Python
256
+ - No requiere privilegios de Docker
257
+
258
+ ## 📊 API Reference
259
+
260
+ ### Clase: `EIPtoNATSBridge`
261
+
262
+ ```python
263
+ bridge = eip2nats.EIPtoNATSBridge(
264
+ plc_address: str,
265
+ nats_url: str,
266
+ nats_subject: str,
267
+ use_binary_format: bool = True
268
+ )
269
+ ```
270
+
271
+ **Métodos:**
272
+ - `start() -> bool`: Inicia el bridge
273
+ - `stop() -> None`: Detiene el bridge
274
+ - `is_running() -> bool`: Estado del bridge
275
+ - `get_received_count() -> int`: Mensajes del PLC
276
+ - `get_published_count() -> int`: Mensajes a NATS
277
+
278
+ ## 🐛 Troubleshooting
279
+
280
+ ### Error: "cannot open shared object file"
281
+
282
+ Aunque el wheel incluye las librerías, verifica RPATH:
283
+
284
+ ```bash
285
+ ldd $(python -c "import eip2nats; print(eip2nats.__file__.replace('__init__.py', 'lib/eip2nats.*.so'))")
286
+ ```
287
+
288
+ Todas las dependencias deberían resolverse localmente.
289
+
290
+ ### Recompilar en otro sistema
291
+
292
+ ```bash
293
+ git clone <repo>
294
+ cd eip2nats
295
+ python scripts/build_dependencies.py
296
+ hatch build
297
+ ```
298
+
299
+ ### Limpiar builds
300
+
301
+ ```bash
302
+ rm -rf build/ dist/ src/eip2nats/lib/
303
+ ```
304
+
305
+ ## 📝 Changelog
306
+
307
+ ### v1.0.0 (2024-02-10)
308
+ - Initial release
309
+ - Self-contained wheel con nats.c y EIPScanner
310
+ - Soporte para formato binario y JSON
311
+ - Thread-safe operations
312
+
313
+ ## 🤝 Contribuir
314
+
315
+ 1. Fork el proyecto
316
+ 2. Crea una rama (`git checkout -b feature/amazing`)
317
+ 3. Commit cambios (`git commit -m 'Add amazing feature'`)
318
+ 4. Push (`git push origin feature/amazing`)
319
+ 5. Abre un Pull Request
320
+
321
+ ## 📄 Licencia
322
+
323
+ MIT License - ver LICENSE file
324
+
325
+ ## 🙏 Créditos
326
+
327
+ - [nats.c](https://github.com/nats-io/nats.c) - Cliente NATS para C
328
+ - [EIPScanner](https://github.com/nimbuscontrols/EIPScanner) - Librería EtherNet/IP
329
+ - [pybind11](https://github.com/pybind/pybind11) - Python bindings
330
+
331
+ ---
332
+
333
+ **Hecho con ❤️ para facilitar la integración industrial**
@@ -0,0 +1,304 @@
1
+ # eip2nats - EtherNet/IP to NATS Bridge
2
+
3
+ Puente completo entre dispositivos EtherNet/IP (PLCs) y servidores NATS, **con todas las dependencias incluidas** en el wheel.
4
+
5
+ ## ✨ Características
6
+
7
+ - ✅ **Self-contained**: Incluye libnats y libEIPScanner compiladas
8
+ - ✅ **Zero dependencies**: No requiere instalación de librerías del sistema
9
+ - ✅ **Entorno virtual**: Compatible con Raspberry Pi OS (sin pip install global)
10
+ - ✅ **Simple instalación**: Setup automático con un comando
11
+ - ✅ **Alto rendimiento**: Bindings nativos C++ con pybind11
12
+ - ✅ **Thread-safe**: Manejo seguro de múltiples conexiones
13
+
14
+ ## 🚀 Instalación Rápida
15
+
16
+ ### Setup Completo Automático
17
+
18
+ ```bash
19
+ ./setup_project.sh
20
+ ```
21
+
22
+ Esto hace TODO automáticamente:
23
+ 1. Crea un entorno virtual en `venv/`
24
+ 2. Instala Hatch y pybind11
25
+ 3. Compila nats.c, EIPScanner y el binding Python
26
+ 4. Crea el wheel
27
+ 5. Instala el wheel en el venv
28
+
29
+ ### Uso Posterior
30
+
31
+ ```bash
32
+ # Activar entorno virtual
33
+ source venv/bin/activate
34
+
35
+ # Ejecutar ejemplo básico
36
+ python examples/basic_example.py
37
+
38
+ # O test completo
39
+ python examples/test_bridge.py
40
+
41
+ # O usar el script helper
42
+ ./run.sh # Ejecuta basic_example.py por defecto
43
+ ./run.sh python examples/test_bridge.py
44
+
45
+ # Desactivar cuando termines
46
+ deactivate
47
+ ```
48
+
49
+ ## 💻 Uso
50
+
51
+ ```python
52
+ import eip2nats
53
+ import time
54
+
55
+ # Crear el bridge
56
+ bridge = eip2nats.EIPtoNATSBridge(
57
+ "192.168.17.200", # IP del PLC
58
+ "nats://192.168.17.138:4222", # Servidor NATS
59
+ "plc.data" # Subject/topic NATS
60
+ )
61
+
62
+ # Iniciar
63
+ if bridge.start():
64
+ print("✅ Bridge corriendo!")
65
+
66
+ # Monitorear
67
+ while bridge.is_running():
68
+ time.sleep(5)
69
+ print(f"📊 RX={bridge.get_received_count()}, "
70
+ f"TX={bridge.get_published_count()}")
71
+
72
+ # Detener
73
+ bridge.stop()
74
+ ```
75
+
76
+ **Ver más ejemplos en [`examples/`](examples/README.md)**
77
+
78
+ ## 📋 Requisitos
79
+
80
+ **Sistema:**
81
+ - Linux (ARM64/x86_64)
82
+ - Python 3.7+
83
+ - git, cmake, make, g++, python3-venv (solo para compilar)
84
+
85
+ **Para desarrollo:** Ejecutar `./setup_project.sh` (crea venv automáticamente)
86
+
87
+ ## 🛠️ Desarrollo
88
+
89
+ ### Modificar Código C++
90
+
91
+ Para desarrollo iterativo sin regenerar el wheel:
92
+
93
+ ```bash
94
+ # 1. Editar código
95
+ nano src/eip2nats/EIPtoNATSBridge.cpp
96
+
97
+ # 2. Opción A: Test C++ standalone (recomendado para debugging)
98
+ ./scripts/build_standalone.sh
99
+ ./test_standalone
100
+
101
+ # 3. Opción B: Compilar binding Python (test de integración)
102
+ ./scripts/build_python_binding.sh
103
+ python examples/basic_example.py
104
+ ```
105
+
106
+ **Ver guía completa:** [`DEVELOPMENT.md`](DEVELOPMENT.md)
107
+
108
+ **Incluye:**
109
+ - Workflow de desarrollo iterativo
110
+ - Debugging con VSCode (recomendado) y GDB
111
+ - Detección de memory leaks con Valgrind
112
+ - Testing C++ standalone vs Python binding
113
+ - Cuándo usar cada enfoque
114
+
115
+ ### Crear Release
116
+
117
+ ```bash
118
+ # Cuando estés satisfecho con los cambios
119
+ hatch build
120
+ ```
121
+
122
+ ### Clonar el repositorio
123
+
124
+ ```bash
125
+ git clone https://github.com/yourusername/eip2nats.git
126
+ cd eip2nats
127
+ ```
128
+
129
+ ### Instalar Hatch
130
+
131
+ ```bash
132
+ pip install hatch
133
+ ```
134
+
135
+ ### Compilar dependencias
136
+
137
+ ```bash
138
+ # Esto descarga y compila nats.c, EIPScanner y el binding Python
139
+ python scripts/build_dependencies.py
140
+ ```
141
+
142
+ O usando Hatch:
143
+
144
+ ```bash
145
+ hatch run build-deps
146
+ ```
147
+
148
+ ### Crear el wheel
149
+
150
+ ```bash
151
+ hatch build
152
+ ```
153
+
154
+ Esto genera:
155
+ - `dist/eip2nats-1.0.0-*.whl` - Wheel con todas las dependencias incluidas
156
+ - `dist/eip2nats-1.0.0.tar.gz` - Source distribution
157
+
158
+ ### Ejecutar tests
159
+
160
+ ```bash
161
+ hatch run test
162
+ ```
163
+
164
+ ## 📦 Estructura del Proyecto
165
+
166
+ ```
167
+ eip2nats/
168
+ ├── pyproject.toml # Configuración Hatch
169
+ ├── README.md
170
+ ├── src/
171
+ │ └── eip2nats/
172
+ │ ├── __init__.py # Package Python
173
+ │ ├── bindings.cpp # Bindings pybind11
174
+ │ ├── EIPtoNATSBridge.h # Header C++
175
+ │ ├── EIPtoNATSBridge.cpp # Implementación C++
176
+ │ └── lib/ # Librerías compiladas (auto-generado)
177
+ │ ├── libnats.so
178
+ │ ├── libEIPScanner.so
179
+ │ └── eip2nats.*.so
180
+ ├── scripts/
181
+ │ └── build_dependencies.py # Script de compilación
182
+ ├── tests/
183
+ │ └── test_basic.py
184
+ └── build/
185
+ └── dependencies/ # Clones de nats.c y EIPScanner
186
+ ├── nats.c/
187
+ └── EIPScanner/
188
+ ```
189
+
190
+ ## 🔧 Cómo Funciona
191
+
192
+ 1. **`scripts/build_dependencies.py`**:
193
+ - Clona nats.c desde GitHub
194
+ - Compila nats.c → `libnats.so`
195
+ - Clona EIPScanner desde GitHub
196
+ - Compila EIPScanner → `libEIPScanner.so`
197
+ - Compila el binding Python → `eip2nats.*.so`
198
+ - Copia todos los `.so` a `src/eip2nats/lib/`
199
+
200
+ 2. **`hatch build`**:
201
+ - Ejecuta el build script automáticamente
202
+ - Empaqueta `src/eip2nats/` completo (código + `.so`)
203
+ - Crea el wheel con RPATH relativo (`$ORIGIN`)
204
+ - El wheel contiene todo lo necesario
205
+
206
+ 3. **`pip install`**:
207
+ - Instala el wheel
208
+ - Los `.so` quedan en el site-packages
209
+ - Python carga las librerías automáticamente
210
+ - ¡Funciona sin dependencias del sistema!
211
+
212
+ ## 🎯 Ventajas de Este Enfoque
213
+
214
+ ### ✅ Comparado con librerías del sistema:
215
+ - No requiere `sudo apt-get install`
216
+ - No hay conflictos de versiones
217
+ - Portabilidad entre sistemas
218
+
219
+ ### ✅ Comparado con wheels normales:
220
+ - Incluye todas las dependencias C/C++
221
+ - Un solo archivo para instalar
222
+ - Funciona en sistemas sin compiladores
223
+
224
+ ### ✅ Comparado con Docker:
225
+ - Más ligero (MBs vs GBs)
226
+ - Integración directa con Python
227
+ - No requiere privilegios de Docker
228
+
229
+ ## 📊 API Reference
230
+
231
+ ### Clase: `EIPtoNATSBridge`
232
+
233
+ ```python
234
+ bridge = eip2nats.EIPtoNATSBridge(
235
+ plc_address: str,
236
+ nats_url: str,
237
+ nats_subject: str,
238
+ use_binary_format: bool = True
239
+ )
240
+ ```
241
+
242
+ **Métodos:**
243
+ - `start() -> bool`: Inicia el bridge
244
+ - `stop() -> None`: Detiene el bridge
245
+ - `is_running() -> bool`: Estado del bridge
246
+ - `get_received_count() -> int`: Mensajes del PLC
247
+ - `get_published_count() -> int`: Mensajes a NATS
248
+
249
+ ## 🐛 Troubleshooting
250
+
251
+ ### Error: "cannot open shared object file"
252
+
253
+ Aunque el wheel incluye las librerías, verifica RPATH:
254
+
255
+ ```bash
256
+ ldd $(python -c "import eip2nats; print(eip2nats.__file__.replace('__init__.py', 'lib/eip2nats.*.so'))")
257
+ ```
258
+
259
+ Todas las dependencias deberían resolverse localmente.
260
+
261
+ ### Recompilar en otro sistema
262
+
263
+ ```bash
264
+ git clone <repo>
265
+ cd eip2nats
266
+ python scripts/build_dependencies.py
267
+ hatch build
268
+ ```
269
+
270
+ ### Limpiar builds
271
+
272
+ ```bash
273
+ rm -rf build/ dist/ src/eip2nats/lib/
274
+ ```
275
+
276
+ ## 📝 Changelog
277
+
278
+ ### v1.0.0 (2024-02-10)
279
+ - Initial release
280
+ - Self-contained wheel con nats.c y EIPScanner
281
+ - Soporte para formato binario y JSON
282
+ - Thread-safe operations
283
+
284
+ ## 🤝 Contribuir
285
+
286
+ 1. Fork el proyecto
287
+ 2. Crea una rama (`git checkout -b feature/amazing`)
288
+ 3. Commit cambios (`git commit -m 'Add amazing feature'`)
289
+ 4. Push (`git push origin feature/amazing`)
290
+ 5. Abre un Pull Request
291
+
292
+ ## 📄 Licencia
293
+
294
+ MIT License - ver LICENSE file
295
+
296
+ ## 🙏 Créditos
297
+
298
+ - [nats.c](https://github.com/nats-io/nats.c) - Cliente NATS para C
299
+ - [EIPScanner](https://github.com/nimbuscontrols/EIPScanner) - Librería EtherNet/IP
300
+ - [pybind11](https://github.com/pybind/pybind11) - Python bindings
301
+
302
+ ---
303
+
304
+ **Hecho con ❤️ para facilitar la integración industrial**
@@ -0,0 +1,321 @@
1
+ #include "EIPtoNATSBridge.h"
2
+ #include "utils/Logger.h"
3
+ #include "utils/Buffer.h"
4
+ #include <sstream>
5
+ #include <iomanip>
6
+ #include <chrono>
7
+
8
+ using namespace bridge;
9
+ using namespace eipScanner;
10
+ using namespace eipScanner::cip;
11
+ using namespace eipScanner::cip::connectionManager;
12
+ using namespace eipScanner::utils;
13
+
14
+ EIPtoNATSBridge::EIPtoNATSBridge(const std::string& plcAddress,
15
+ const std::string& natsUrl,
16
+ const std::string& natsSubject,
17
+ bool useBinaryFormat)
18
+ : plcAddress_(plcAddress)
19
+ , natsUrl_(natsUrl)
20
+ , natsSubject_(natsSubject)
21
+ , useBinaryFormat_(useBinaryFormat)
22
+ , natsConn_(nullptr)
23
+ , natsOpts_(nullptr)
24
+ , connectionManager_(nullptr)
25
+ , running_(false)
26
+ , shouldStop_(false)
27
+ , publishedCount_(0)
28
+ , receivedCount_(0)
29
+ {
30
+ Logger(LogLevel::INFO) << "EIPtoNATSBridge creado - PLC: " << plcAddress
31
+ << " NATS: " << natsUrl
32
+ << " Subject: " << natsSubject
33
+ << " Formato: " << (useBinaryFormat ? "Binario" : "JSON");
34
+ }
35
+
36
+ EIPtoNATSBridge::~EIPtoNATSBridge() {
37
+ if (running_) {
38
+ Logger(LogLevel::WARNING) << "Bridge destruido mientras estaba corriendo - deteniendo...";
39
+ stop();
40
+ }
41
+ }
42
+
43
+ bool EIPtoNATSBridge::start() {
44
+ if (running_) {
45
+ Logger(LogLevel::WARNING) << "Bridge ya está corriendo";
46
+ return false;
47
+ }
48
+
49
+ Logger(LogLevel::INFO) << "Iniciando EIPtoNATS Bridge...";
50
+
51
+ // Inicializar NATS primero
52
+ if (!initNATS()) {
53
+ Logger(LogLevel::ERROR) << "Fallo al inicializar NATS";
54
+ return false;
55
+ }
56
+
57
+ // Inicializar EIP
58
+ if (!initEIP()) {
59
+ Logger(LogLevel::ERROR) << "Fallo al inicializar EIP";
60
+ closeNATS();
61
+ return false;
62
+ }
63
+
64
+ // Arrancar el thread worker
65
+ shouldStop_ = false;
66
+ running_ = true;
67
+ workerThread_ = std::thread(&EIPtoNATSBridge::workerLoop, this);
68
+
69
+ Logger(LogLevel::INFO) << "✅ Bridge iniciado exitosamente";
70
+ return true;
71
+ }
72
+
73
+ void EIPtoNATSBridge::stop() {
74
+ if (!running_) {
75
+ Logger(LogLevel::WARNING) << "Bridge ya está detenido";
76
+ return;
77
+ }
78
+
79
+ Logger(LogLevel::INFO) << "Deteniendo EIPtoNATS Bridge...";
80
+
81
+ // Señalizar al thread que debe detenerse
82
+ shouldStop_ = true;
83
+
84
+ // Esperar a que el thread termine
85
+ if (workerThread_.joinable()) {
86
+ workerThread_.join();
87
+ }
88
+
89
+ // Cerrar conexiones
90
+ closeEIP();
91
+ closeNATS();
92
+
93
+ running_ = false;
94
+
95
+ Logger(LogLevel::INFO) << "✅ Bridge detenido - Mensajes recibidos: "
96
+ << receivedCount_ << " - Mensajes publicados: "
97
+ << publishedCount_;
98
+ }
99
+
100
+ bool EIPtoNATSBridge::isRunning() const {
101
+ return running_;
102
+ }
103
+
104
+ uint64_t EIPtoNATSBridge::getPublishedCount() const {
105
+ return publishedCount_;
106
+ }
107
+
108
+ uint64_t EIPtoNATSBridge::getReceivedCount() const {
109
+ return receivedCount_;
110
+ }
111
+
112
+ bool EIPtoNATSBridge::initNATS() {
113
+ Logger(LogLevel::INFO) << "Conectando a NATS: " << natsUrl_;
114
+
115
+ natsStatus s;
116
+
117
+ // Crear opciones
118
+ s = natsOptions_Create(&natsOpts_);
119
+ if (s != NATS_OK) {
120
+ Logger(LogLevel::ERROR) << "Error creando opciones NATS: " << natsStatus_GetText(s);
121
+ return false;
122
+ }
123
+
124
+ // Configurar URL
125
+ s = natsOptions_SetURL(natsOpts_, natsUrl_.c_str());
126
+ if (s != NATS_OK) {
127
+ Logger(LogLevel::ERROR) << "Error configurando URL NATS: " << natsStatus_GetText(s);
128
+ natsOptions_Destroy(natsOpts_);
129
+ natsOpts_ = nullptr;
130
+ return false;
131
+ }
132
+
133
+ // Configurar timeout
134
+ s = natsOptions_SetTimeout(natsOpts_, 5000);
135
+ if (s != NATS_OK) {
136
+ Logger(LogLevel::ERROR) << "Error configurando timeout NATS: " << natsStatus_GetText(s);
137
+ natsOptions_Destroy(natsOpts_);
138
+ natsOpts_ = nullptr;
139
+ return false;
140
+ }
141
+
142
+ // Conectar
143
+ s = natsConnection_Connect(&natsConn_, natsOpts_);
144
+ if (s != NATS_OK) {
145
+ Logger(LogLevel::ERROR) << "Error conectando a NATS: " << natsStatus_GetText(s);
146
+ natsOptions_Destroy(natsOpts_);
147
+ natsOpts_ = nullptr;
148
+ return false;
149
+ }
150
+
151
+ Logger(LogLevel::INFO) << "✅ Conectado a NATS exitosamente";
152
+ return true;
153
+ }
154
+
155
+ bool EIPtoNATSBridge::initEIP() {
156
+ Logger(LogLevel::INFO) << "Conectando a PLC EIP: " << plcAddress_;
157
+
158
+ try {
159
+ // Crear SessionInfo
160
+ sessionInfo_ = std::make_shared<SessionInfo>(plcAddress_, 0xAF12);
161
+
162
+ // Crear ConnectionManager
163
+ connectionManager_ = std::make_unique<ConnectionManager>();
164
+
165
+ // Configurar parámetros de conexión (basado en tu código original)
166
+ ConnectionParameters parameters;
167
+ parameters.connectionPath = {0x20, 0x04, 0x24, 4, 0x2C, 2, 0x2C, 1};
168
+ parameters.o2tRealTimeFormat = true;
169
+ parameters.originatorVendorId = 342;
170
+ parameters.originatorSerialNumber = 0x12345;
171
+
172
+ parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::P2P;
173
+ parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY;
174
+ parameters.t2oNetworkConnectionParams |= 100;
175
+
176
+ parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::P2P;
177
+ parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY;
178
+ parameters.o2tNetworkConnectionParams |= 0;
179
+
180
+ parameters.o2tRPI = 2000;
181
+ parameters.t2oRPI = 2000;
182
+ parameters.transportTypeTrigger |= NetworkConnectionParams::CLASS1 | NetworkConnectionParams::TRIG_CYCLIC;
183
+
184
+ // Abrir conexión
185
+ ioConnection_ = connectionManager_->forwardOpen(sessionInfo_, parameters);
186
+
187
+ if (auto ptr = ioConnection_.lock()) {
188
+ // Configurar listener para datos recibidos
189
+ ptr->setReceiveDataListener([this](auto realTimeHeader, auto sequence, auto data) {
190
+ this->onEIPDataReceived(realTimeHeader, sequence, data);
191
+ });
192
+
193
+ // Configurar listener para cierre de conexión
194
+ ptr->setCloseListener([this]() {
195
+ Logger(LogLevel::WARNING) << "Conexión EIP cerrada por el PLC";
196
+ shouldStop_ = true;
197
+ });
198
+
199
+ Logger(LogLevel::INFO) << "✅ Conexión EIP abierta exitosamente";
200
+ return true;
201
+ } else {
202
+ Logger(LogLevel::ERROR) << "Error: No se pudo obtener el puntero de IOConnection";
203
+ return false;
204
+ }
205
+
206
+ } catch (const std::exception& e) {
207
+ Logger(LogLevel::ERROR) << "Excepción al inicializar EIP: " << e.what();
208
+ return false;
209
+ }
210
+ }
211
+
212
+ void EIPtoNATSBridge::closeNATS() {
213
+ std::lock_guard<std::mutex> lock(natsMutex_);
214
+
215
+ if (natsConn_ != nullptr) {
216
+ Logger(LogLevel::INFO) << "Cerrando conexión NATS...";
217
+ natsConnection_Destroy(natsConn_);
218
+ natsConn_ = nullptr;
219
+ }
220
+
221
+ if (natsOpts_ != nullptr) {
222
+ natsOptions_Destroy(natsOpts_);
223
+ natsOpts_ = nullptr;
224
+ }
225
+ }
226
+
227
+ void EIPtoNATSBridge::closeEIP() {
228
+ Logger(LogLevel::INFO) << "Cerrando conexión EIP...";
229
+
230
+ if (connectionManager_ && sessionInfo_) {
231
+ try {
232
+ connectionManager_->forwardClose(sessionInfo_, ioConnection_);
233
+ Logger(LogLevel::INFO) << "Forward Close enviado";
234
+ } catch (const std::exception& e) {
235
+ Logger(LogLevel::ERROR) << "Error en forward close: " << e.what();
236
+ }
237
+ }
238
+
239
+ ioConnection_.reset();
240
+ connectionManager_.reset();
241
+ sessionInfo_.reset();
242
+ }
243
+
244
+ void EIPtoNATSBridge::workerLoop() {
245
+ Logger(LogLevel::INFO) << "Thread worker iniciado";
246
+
247
+ while (!shouldStop_ && connectionManager_->hasOpenConnections()) {
248
+ // Manejar las conexiones EIP (procesar datos entrantes)
249
+ connectionManager_->handleConnections(std::chrono::milliseconds(1));
250
+ }
251
+
252
+ Logger(LogLevel::INFO) << "Thread worker finalizando";
253
+ }
254
+
255
+ bool EIPtoNATSBridge::publishToNATS(const std::vector<uint8_t>& data) {
256
+ std::lock_guard<std::mutex> lock(natsMutex_);
257
+
258
+ if (natsConn_ == nullptr) {
259
+ Logger(LogLevel::ERROR) << "No hay conexión NATS activa";
260
+ return false;
261
+ }
262
+
263
+ natsStatus s;
264
+
265
+ if (useBinaryFormat_) {
266
+ // Publicar datos binarios directamente (más eficiente)
267
+ s = natsConnection_Publish(natsConn_,
268
+ natsSubject_.c_str(),
269
+ data.data(),
270
+ data.size());
271
+ } else {
272
+ // Publicar como JSON (para debugging o interoperabilidad)
273
+ std::ostringstream jsonStream;
274
+ jsonStream << "{\"timestamp\":" << time(nullptr)
275
+ << ",\"sequence\":" << receivedCount_
276
+ << ",\"size\":" << data.size()
277
+ << ",\"data\":\"";
278
+
279
+ // Convertir bytes a hexadecimal
280
+ for (const auto& byte : data) {
281
+ jsonStream << std::hex << std::setfill('0') << std::setw(2) << (int)byte;
282
+ }
283
+
284
+ jsonStream << "\"}";
285
+ std::string jsonStr = jsonStream.str();
286
+
287
+ s = natsConnection_PublishString(natsConn_, natsSubject_.c_str(), jsonStr.c_str());
288
+ }
289
+
290
+ if (s == NATS_OK) {
291
+ publishedCount_++;
292
+ Logger(LogLevel::DEBUG) << "Publicado a NATS [" << publishedCount_ << "]: "
293
+ << data.size() << " bytes ("
294
+ << (useBinaryFormat_ ? "binario" : "JSON") << ")";
295
+ return true;
296
+ } else {
297
+ Logger(LogLevel::ERROR) << "Error publicando a NATS: " << natsStatus_GetText(s);
298
+ return false;
299
+ }
300
+ }
301
+
302
+ void EIPtoNATSBridge::onEIPDataReceived(uint32_t realTimeHeader,
303
+ uint16_t sequence,
304
+ const std::vector<uint8_t>& data) {
305
+ receivedCount_++;
306
+
307
+ // Log detallado de lo recibido
308
+ std::ostringstream ss;
309
+ ss << "EIP RX [" << receivedCount_ << "] seq=" << sequence
310
+ << " size=" << data.size() << " data=";
311
+ for (const auto& byte : data) {
312
+ ss << std::hex << std::setfill('0') << std::setw(2) << (int)byte << " ";
313
+ }
314
+
315
+ Logger(LogLevel::DEBUG) << ss.str();
316
+
317
+ // Publicar a NATS
318
+ if (!publishToNATS(data)) {
319
+ Logger(LogLevel::WARNING) << "Fallo al publicar datos a NATS";
320
+ }
321
+ }
@@ -0,0 +1,141 @@
1
+ #ifndef EIP_TO_NATS_BRIDGE_H
2
+ #define EIP_TO_NATS_BRIDGE_H
3
+
4
+ #include <memory>
5
+ #include <thread>
6
+ #include <atomic>
7
+ #include <mutex>
8
+ #include <string>
9
+ #include <vector>
10
+ #include <nats/nats.h>
11
+ #include <cip/connectionManager/NetworkConnectionParams.h>
12
+ #include "SessionInfo.h"
13
+ #include "ConnectionManager.h"
14
+
15
+ namespace bridge {
16
+
17
+ /**
18
+ * @brief Puente entre EtherNet/IP (usando EIPScanner) y NATS
19
+ *
20
+ * Esta clase gestiona una conexión EIP implícita y publica los datos
21
+ * recibidos a un servidor NATS en un thread separado.
22
+ */
23
+ class EIPtoNATSBridge {
24
+ public:
25
+ /**
26
+ * @brief Constructor
27
+ * @param plcAddress Dirección IP del PLC
28
+ * @param natsUrl URL del servidor NATS (ej: "nats://192.168.17.138:4222")
29
+ * @param natsSubject Subject/topic donde publicar los datos
30
+ * @param useBinaryFormat Si es true usa binario, si es false usa JSON (default: true)
31
+ */
32
+ EIPtoNATSBridge(const std::string& plcAddress,
33
+ const std::string& natsUrl,
34
+ const std::string& natsSubject,
35
+ bool useBinaryFormat = true);
36
+
37
+ /**
38
+ * @brief Destructor - asegura que todo esté limpiamente cerrado
39
+ */
40
+ ~EIPtoNATSBridge();
41
+
42
+ /**
43
+ * @brief Inicia el bridge: conecta a NATS, abre conexión EIP y arranca el thread
44
+ * @return true si todo se inició correctamente, false en caso de error
45
+ */
46
+ bool start();
47
+
48
+ /**
49
+ * @brief Detiene el bridge: cierra conexión EIP, desconecta NATS y detiene el thread
50
+ */
51
+ void stop();
52
+
53
+ /**
54
+ * @brief Verifica si el bridge está corriendo
55
+ * @return true si está activo, false si está detenido
56
+ */
57
+ bool isRunning() const;
58
+
59
+ /**
60
+ * @brief Obtiene el número de mensajes publicados
61
+ * @return Contador de mensajes enviados a NATS
62
+ */
63
+ uint64_t getPublishedCount() const;
64
+
65
+ /**
66
+ * @brief Obtiene el número de mensajes recibidos del PLC
67
+ * @return Contador de mensajes recibidos por EIP
68
+ */
69
+ uint64_t getReceivedCount() const;
70
+
71
+ private:
72
+ // Configuración
73
+ std::string plcAddress_;
74
+ std::string natsUrl_;
75
+ std::string natsSubject_;
76
+ bool useBinaryFormat_;
77
+
78
+ // NATS
79
+ natsConnection* natsConn_;
80
+ natsOptions* natsOpts_;
81
+ std::mutex natsMutex_;
82
+
83
+ // EIP Scanner
84
+ std::shared_ptr<eipScanner::SessionInfo> sessionInfo_;
85
+ std::unique_ptr<eipScanner::ConnectionManager> connectionManager_;
86
+ std::weak_ptr<eipScanner::IOConnection> ioConnection_;
87
+
88
+ // Thread control
89
+ std::thread workerThread_;
90
+ std::atomic<bool> running_;
91
+ std::atomic<bool> shouldStop_;
92
+
93
+ // Estadísticas
94
+ std::atomic<uint64_t> publishedCount_;
95
+ std::atomic<uint64_t> receivedCount_;
96
+
97
+ /**
98
+ * @brief Función principal del thread worker
99
+ */
100
+ void workerLoop();
101
+
102
+ /**
103
+ * @brief Inicializa la conexión NATS
104
+ * @return true si se conectó correctamente
105
+ */
106
+ bool initNATS();
107
+
108
+ /**
109
+ * @brief Inicializa la conexión EIP
110
+ * @return true si se conectó correctamente
111
+ */
112
+ bool initEIP();
113
+
114
+ /**
115
+ * @brief Cierra la conexión NATS
116
+ */
117
+ void closeNATS();
118
+
119
+ /**
120
+ * @brief Cierra la conexión EIP
121
+ */
122
+ void closeEIP();
123
+
124
+ /**
125
+ * @brief Publica datos a NATS
126
+ * @param data Vector de bytes a publicar
127
+ * @return true si se publicó correctamente
128
+ */
129
+ bool publishToNATS(const std::vector<uint8_t>& data);
130
+
131
+ /**
132
+ * @brief Callback para datos recibidos del PLC
133
+ */
134
+ void onEIPDataReceived(uint32_t realTimeHeader,
135
+ uint16_t sequence,
136
+ const std::vector<uint8_t>& data);
137
+ };
138
+
139
+ } // namespace bridge
140
+
141
+ #endif // EIP_TO_NATS_BRIDGE_H
@@ -0,0 +1,42 @@
1
+ """
2
+ eip2nats - EtherNet/IP to NATS Bridge
3
+
4
+ Puente entre dispositivos EtherNet/IP (PLCs) y servidores NATS con
5
+ todas las dependencias incluidas.
6
+ """
7
+
8
+ import os
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ # Agregar el directorio lib al path de búsqueda de librerías
13
+ _lib_dir = Path(__file__).parent / "lib"
14
+ if _lib_dir.exists():
15
+ # Agregar al LD_LIBRARY_PATH en tiempo de ejecución
16
+ if sys.platform.startswith('linux'):
17
+ os.environ['LD_LIBRARY_PATH'] = f"{_lib_dir}:{os.environ.get('LD_LIBRARY_PATH', '')}"
18
+
19
+ # Importar el módulo C++ - está directamente en este directorio
20
+ try:
21
+ # Buscar el archivo .so en el directorio actual
22
+ import importlib.util
23
+ _module_dir = Path(__file__).parent
24
+
25
+ # Buscar el módulo compilado (eip_nats_bridge*.so)
26
+ for so_file in _module_dir.glob("eip_nats_bridge*.so"):
27
+ if so_file.is_file():
28
+ # El nombre debe coincidir con PYBIND11_MODULE(eip_nats_bridge, m)
29
+ spec = importlib.util.spec_from_file_location("eip_nats_bridge", so_file)
30
+ if spec and spec.loader:
31
+ module = importlib.util.module_from_spec(spec)
32
+ spec.loader.exec_module(module)
33
+ EIPtoNATSBridge = module.EIPtoNATSBridge
34
+ break
35
+ else:
36
+ raise ImportError("No se encontró el módulo eip_nats_bridge compilado (.so)")
37
+
38
+ except ImportError as e:
39
+ raise ImportError(f"Error cargando el módulo eip2nats: {e}")
40
+
41
+ __version__ = "1.0.0"
42
+ __all__ = ["EIPtoNATSBridge"]
@@ -0,0 +1,66 @@
1
+ #include <pybind11/pybind11.h>
2
+ #include <pybind11/stl.h>
3
+ #include "EIPtoNATSBridge.h"
4
+
5
+ namespace py = pybind11;
6
+
7
+ PYBIND11_MODULE(eip_nats_bridge, m) {
8
+ m.doc() = "EIP to NATS Bridge - Puente entre EtherNet/IP y NATS";
9
+
10
+ py::class_<bridge::EIPtoNATSBridge>(m, "EIPtoNATSBridge")
11
+ .def(py::init<const std::string&, const std::string&, const std::string&>(),
12
+ py::arg("plc_address"),
13
+ py::arg("nats_url"),
14
+ py::arg("nats_subject"),
15
+ "Constructor del bridge\n\n"
16
+ "Args:\n"
17
+ " plc_address (str): Dirección IP del PLC (ej: '192.168.17.200')\n"
18
+ " nats_url (str): URL del servidor NATS (ej: 'nats://192.168.17.138:4222')\n"
19
+ " nats_subject (str): Subject/topic NATS (ej: 'plc.data')")
20
+
21
+ .def(py::init<const std::string&, const std::string&, const std::string&, bool>(),
22
+ py::arg("plc_address"),
23
+ py::arg("nats_url"),
24
+ py::arg("nats_subject"),
25
+ py::arg("use_binary_format"),
26
+ "Constructor del bridge con formato personalizado\n\n"
27
+ "Args:\n"
28
+ " plc_address (str): Dirección IP del PLC\n"
29
+ " nats_url (str): URL del servidor NATS\n"
30
+ " nats_subject (str): Subject/topic NATS\n"
31
+ " use_binary_format (bool): True para binario, False para JSON")
32
+
33
+ .def("start", &bridge::EIPtoNATSBridge::start,
34
+ "Inicia el bridge: conecta a NATS, abre conexión EIP y arranca el thread\n\n"
35
+ "Returns:\n"
36
+ " bool: True si se inició correctamente, False en caso de error")
37
+
38
+ .def("stop", &bridge::EIPtoNATSBridge::stop,
39
+ "Detiene el bridge: cierra conexión EIP, desconecta NATS y detiene el thread")
40
+
41
+ .def("is_running", &bridge::EIPtoNATSBridge::isRunning,
42
+ "Verifica si el bridge está corriendo\n\n"
43
+ "Returns:\n"
44
+ " bool: True si está activo, False si está detenido")
45
+
46
+ .def("get_published_count", &bridge::EIPtoNATSBridge::getPublishedCount,
47
+ "Obtiene el número de mensajes publicados a NATS\n\n"
48
+ "Returns:\n"
49
+ " int: Contador de mensajes enviados")
50
+
51
+ .def("get_received_count", &bridge::EIPtoNATSBridge::getReceivedCount,
52
+ "Obtiene el número de mensajes recibidos del PLC\n\n"
53
+ "Returns:\n"
54
+ " int: Contador de mensajes recibidos")
55
+
56
+ .def("__repr__", [](const bridge::EIPtoNATSBridge &bridge) {
57
+ return "<EIPtoNATSBridge running=" +
58
+ std::string(bridge.isRunning() ? "True" : "False") +
59
+ " received=" + std::to_string(bridge.getReceivedCount()) +
60
+ " published=" + std::to_string(bridge.getPublishedCount()) + ">";
61
+ });
62
+
63
+ // Información del módulo
64
+ m.attr("__version__") = "1.0.0";
65
+ m.attr("__author__") = "Your Name";
66
+ }
@@ -0,0 +1,95 @@
1
+ [build-system]
2
+ requires = ["hatchling", "pybind11>=2.6.0"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "eip2nats"
7
+ version = "1.0.0"
8
+ description = "EtherNet/IP to NATS Bridge with bundled dependencies"
9
+ readme = "README.md"
10
+ requires-python = ">=3.7"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "Your Name", email = "your.email@example.com"},
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.7",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: C++",
26
+ "Topic :: Software Development :: Libraries",
27
+ ]
28
+
29
+ dependencies = [
30
+ "pybind11>=2.6.0",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ dev = [
35
+ "pytest>=7.0",
36
+ "black>=22.0",
37
+ "ruff>=0.0.243",
38
+ ]
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/yourusername/eip2nats"
42
+ Documentation = "https://github.com/yourusername/eip2nats/blob/main/README.md"
43
+ Repository = "https://github.com/yourusername/eip2nats"
44
+
45
+ [tool.hatch.build]
46
+ packages = ["src/eip2nats"]
47
+ # Excluir archivos fuente C++, solo incluir Python y .so
48
+ exclude = [
49
+ "src/eip2nats/*.cpp",
50
+ "src/eip2nats/*.h",
51
+ "src/eip2nats/*.hpp",
52
+ ]
53
+
54
+ [tool.hatch.build.targets.wheel]
55
+ packages = ["src/eip2nats"]
56
+ # Incluir el módulo Python compilado y las librerías auxiliares
57
+ artifacts = [
58
+ "src/eip2nats/eip_nats_bridge*.so",
59
+ "src/eip2nats/lib/*.so*",
60
+ ]
61
+ # Excluir archivos fuente del wheel
62
+ exclude = [
63
+ "src/eip2nats/*.cpp",
64
+ "src/eip2nats/*.h",
65
+ "src/eip2nats/*.hpp",
66
+ ]
67
+
68
+ [tool.hatch.build.targets.sdist]
69
+ exclude = [
70
+ "/.git",
71
+ "/build",
72
+ "/dist",
73
+ "/venv",
74
+ "*.pyc",
75
+ "__pycache__",
76
+ ]
77
+
78
+ [tool.hatch.envs.default]
79
+ dependencies = [
80
+ "pytest",
81
+ "pytest-cov",
82
+ ]
83
+
84
+ [tool.hatch.envs.default.scripts]
85
+ build-deps = "python scripts/build_dependencies.py"
86
+ test = "pytest {args:tests}"
87
+ cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=src/eip2nats {args:tests}"
88
+
89
+ [tool.black]
90
+ line-length = 100
91
+ target-version = ['py37']
92
+
93
+ [tool.ruff]
94
+ line-length = 100
95
+ select = ["E", "F", "W", "I", "N"]