Skip to content

Commit 3ab8ac1

Browse files
committed
Actualización del gestor de empaquetado para librerías, con soporte a Arduino IDE y PlatformIO
1 parent ab0c7c1 commit 3ab8ac1

2 files changed

Lines changed: 166 additions & 16 deletions

File tree

miniml/exporters/library_packer.py

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
"""
2-
core/exporters/library_packer.py
3-
--------------------------------
4-
Gestor de empaquetado de librerías para EduBot Pro.
5-
Convierte código fuente suelto en librerías .ZIP estándares
6-
compatibles con el Gestor de Librerías de Arduino IDE.
2+
Gestor de empaquetado de librerías para MiniML Engine.
3+
Genera estructuras duales compatibles con PlatformIO (library.json)
4+
y Arduino IDE (library.properties), resolviendo problemas de linkeo.
75
"""
86

97
import os
@@ -13,40 +11,111 @@
1311
class LibraryPackager:
1412

1513
@staticmethod
16-
def create_arduino_zip(model_name, cpp_code, version="1.0.0", quantized=False, force=False):
14+
def create_arduino_zip(model_name, cpp_code, version="1.0.0", quantized=False, force=False, license_type="Apache-2.0"):
1715
"""
18-
Crea ZIP Arduino. Si quantized=True, añade flag "Pro" en descripción.
16+
Crea ZIP estructurado para PlatformIO y Arduino.
1917
"""
2018
safe_name = "".join(c for c in model_name if c.isalnum() or c == '_')
2119
lib_name = f"MiniML_{safe_name}"
2220
zip_filename = f"{lib_name}_v{version}.zip"
21+
2322
export_path = os.path.join("exports", zip_filename)
2423
os.makedirs("exports", exist_ok=True)
24+
2525
if os.path.exists(export_path) and not force:
2626
return {"success": False, "requires_confirm": True, "error": f"La librería '{zip_filename}' ya existe. ¿Deseas reemplazarla?"}
2727

2828
desc_extra = " (INT8 Quantized)" if quantized else ""
29+
year = datetime.now().year
2930

3031
try:
3132
with zipfile.ZipFile(export_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
32-
zipf.writestr(f"{lib_name}/src/{lib_name}.h", cpp_code)
3333

34+
# Código Fuente (Estructura Dual)
35+
# Guardamos en src/ para que ambos entornos lo detecten
36+
header_path = f"{lib_name}/src/{lib_name}.h"
37+
zipf.writestr(header_path, cpp_code)
38+
39+
# Archivo .cpp auxiliar para evitar "Multiple Definitions" si se incluye en varios archivos
40+
cpp_stub = f"""#include "{lib_name}.h"\n// El motor MiniML opera de forma estática (Header-Only).\n"""
41+
zipf.writestr(f"{lib_name}/src/{lib_name}.cpp", cpp_stub)
42+
43+
# Manifiesto para Arduino IDE
3444
props = (
3545
f"name={lib_name}\n"
3646
f"version={version}\n"
37-
f"author=MiniML Engine\n"
38-
f"maintainer=User\n"
39-
f"sentence=TinyML Model{desc_extra} generated by MiniML Engine.\n"
40-
f"paragraph=Optimized inference engine for Arduino.\n"
47+
f"author=MiniML Engine Architect\n"
48+
f"maintainer=MiniML Developer\n"
49+
f"sentence=Edge AI Model{desc_extra} generated by MiniML Engine.\n"
50+
f"paragraph=Optimized C++ inference engine for microcontrollers.\n"
4151
f"category=Data Processing\n"
42-
f"url=https://github.com/Shuuida/MiniML-Engine\n"
52+
f"url=https://github.com/MiniML-Engine\n"
4353
f"architectures=*\n"
54+
f"depends=\n"
4455
)
4556
zipf.writestr(f"{lib_name}/library.properties", props)
4657

47-
# Ejemplo .ino simple
48-
example = f"""#include "{lib_name}.h"\n{safe_name} model;\nvoid setup() {{ Serial.begin(9600); }}\nvoid loop() {{}}"""
49-
zipf.writestr(f"{lib_name}/examples/Inference/Inference.ino", example)
58+
# Manifiesto para PlatformIO (library.json)
59+
pio_json = f"""{{
60+
"name": "{lib_name}",
61+
"version": "{version}",
62+
"description": "Edge AI Model{desc_extra} generated by MiniML Engine",
63+
"keywords": ["machine-learning", "tinyml", "ai", "miniml"],
64+
"repository": {{
65+
"type": "git",
66+
"url": "https://github.com/MiniML-Engine"
67+
}},
68+
"authors": [
69+
{{
70+
"name": "MiniML Engine Architect"
71+
}}
72+
],
73+
"license": "{license_type}",
74+
"frameworks": ["arduino", "espidf", "mbed"],
75+
"platforms": ["atmelavr", "espressif32", "ststm32", "nordicnrf52"],
76+
"build": {{
77+
"flags": ["-O3"]
78+
}}
79+
}}"""
80+
zipf.writestr(f"{lib_name}/library.json", pio_json)
81+
82+
# Inyección de Licencia Open Source (Apache 2.0 por defecto)
83+
license_text = f"""Copyright {year} MiniML Engine Developer
84+
85+
Licensed under the Apache License, Version 2.0 (the "License");
86+
you may not use this file except in compliance with the License.
87+
You may obtain a copy of the License at
88+
89+
http://www.apache.org/licenses/LICENSE-2.0
90+
91+
Unless required by applicable law or agreed to in writing, software
92+
distributed under the License is distributed on an "AS IS" BASIS,
93+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
94+
See the License for the specific language governing permissions and
95+
limitations under the License.
96+
"""
97+
zipf.writestr(f"{lib_name}/LICENSE", license_text)
98+
99+
# Ejemplo de Integración
100+
example = f"""/*
101+
* MiniML Engine - Inferencia Edge
102+
* Modelo: {safe_name}
103+
*/
104+
#include <Arduino.h>
105+
#include "{lib_name}.h"
106+
107+
void setup() {{
108+
Serial.begin(115200);
109+
Serial.println("MiniML Model Initialized");
110+
}}
111+
112+
void loop() {{
113+
// float input_data[] = {{ ... }};
114+
// float prediction = predict(input_data);
115+
delay(1000);
116+
}}
117+
"""
118+
zipf.writestr(f"{lib_name}/examples/BasicInference/BasicInference.ino", example)
50119

51120
return {"success": True, "path": export_path}
52121

tests/test_packer.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
"""
2+
===================================================
3+
Test de QA: Empaquetador de Librerías
4+
Evalúa: Generación de ZIP, estructura dual (src/include),
5+
manifiestos (JSON/Properties) y licencias.
6+
"""
7+
8+
import os
9+
import zipfile
10+
from miniml.exporters.library_packer import LibraryPackager
11+
12+
print("1. Generando código C++ de prueba (Dummy Model)...")
13+
dummy_cpp_code = """
14+
// --- Dummy MiniML Model para pruebas de empaquetado ---
15+
#ifndef MINIML_TEST_H
16+
#define MINIML_TEST_H
17+
18+
float predict_dummy(float* input) {
19+
return 0.99f; // Predicción simulada
20+
}
21+
22+
#endif
23+
"""
24+
25+
print("2. Ejecutando LibraryPackager...")
26+
model_name = "QA_Model_Test"
27+
# Llamamos al empaquetador simulando un modelo cuantizado
28+
result = LibraryPackager.create_arduino_zip(
29+
model_name=model_name,
30+
cpp_code=dummy_cpp_code,
31+
version="2.0.0",
32+
quantized=True,
33+
force=True # Forzamos sobreescritura si ya existe
34+
)
35+
36+
if not result["success"]:
37+
print(f"\n[ERROR] Falló el empaquetado: {result.get('error')}")
38+
else:
39+
zip_path = result["path"]
40+
print(f"\n[OK] Librería ZIP generada en: {zip_path}")
41+
42+
print("\n3. Auditando estructura interna del archivo ZIP...")
43+
print("--------------------------------------------------")
44+
45+
# Abrimos el ZIP recién creado en modo lectura
46+
try:
47+
with zipfile.ZipFile(zip_path, 'r') as zipf:
48+
# Listamos todos los archivos dentro del ZIP
49+
file_list = zipf.namelist()
50+
51+
# Imprimimos el árbol de archivos ordenado
52+
for file_name in sorted(file_list):
53+
# Formateo simple para simular un árbol de directorios
54+
depth = file_name.count('/')
55+
indent = " " * (depth - 1) if depth > 0 else ""
56+
marker = "|-- " if depth > 0 else ""
57+
58+
# Leer tamaño del archivo para verificar que no esté vacío
59+
file_info = zipf.getinfo(file_name)
60+
size_kb = file_info.file_size / 1024.0
61+
62+
print(f"{indent}{marker}{os.path.basename(file_name)} ({size_kb:.2f} KB)")
63+
64+
# Opcional: Leer y validar una clave del library.json
65+
if "library.json" in file_name:
66+
json_content = zipf.read(file_name).decode('utf-8')
67+
if "espressif32" in json_content and "atmelavr" in json_content:
68+
print(f"{indent} * [Validado] Plataformas PlatformIO detectadas.")
69+
70+
# Opcional: Validar Licencia
71+
if "LICENSE" in file_name:
72+
lic_content = zipf.read(file_name).decode('utf-8')
73+
if "Apache License" in lic_content:
74+
print(f"{indent} * [Validado] Licencia Apache 2.0 inyectada.")
75+
76+
print("--------------------------------------------------")
77+
print("Auditoría de Empaquetado Finalizada con Éxito.")
78+
print("La librería está lista para ser importada en PlatformIO o Arduino IDE.")
79+
80+
except Exception as e:
81+
print(f"\n[ERROR] No se pudo leer el archivo ZIP: {e}")

0 commit comments

Comments
 (0)