@hybridlabor-api/bdb-hardware-pcb 0.1.0
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.
- package/README.md +116 -0
- package/config/mcp/antigravity.json +22 -0
- package/config/mcp/claude.json +22 -0
- package/config/mcp/codex.toml +25 -0
- package/docs/adr/ADR-001-KICAD-OPENSCAD-MCP-STRATEGY.md +334 -0
- package/docs/review_documentation.md +121 -0
- package/installer.js +358 -0
- package/mcp_servers/kicad-mcp-server/.env.example +22 -0
- package/mcp_servers/kicad-mcp-server/.github/workflows/ci.yml +36 -0
- package/mcp_servers/kicad-mcp-server/CLAUDE.md +487 -0
- package/mcp_servers/kicad-mcp-server/README.md +316 -0
- package/mcp_servers/kicad-mcp-server/docs/DEVICE_TREE.md +416 -0
- package/mcp_servers/kicad-mcp-server/docs/INSTALLATION.md +332 -0
- package/mcp_servers/kicad-mcp-server/docs/PIN_ANALYSIS.md +332 -0
- package/mcp_servers/kicad-mcp-server/docs/README.md +240 -0
- package/mcp_servers/kicad-mcp-server/docs/TESTING.md +613 -0
- package/mcp_servers/kicad-mcp-server/docs/VALIDATION.md +268 -0
- package/mcp_servers/kicad-mcp-server/pyproject.toml +96 -0
- package/mcp_servers/kicad-mcp-server/requirements-dev.txt +16 -0
- package/mcp_servers/kicad-mcp-server/requirements-test.txt +24 -0
- package/mcp_servers/kicad-mcp-server/requirements.txt +13 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__init__.py +3 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__main__.py +17 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/config.py +46 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/types.py +87 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/netlist_parser.py +234 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser.py +375 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser_kicad.py +327 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/schematic_parser.py +902 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/server.py +71 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/arduino/connectivity_test.cpp.j2 +189 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/atmega.dts.j2 +77 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/esp32.dts.j2 +77 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/nrf52.dts.j2 +77 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/stm32f4.dts.j2 +89 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/esp_idf/test_suite.c.j2 +340 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/pytest/test_connectivity.py.j2 +147 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/st_hal/hal_test.c.j2 +313 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_gpio_test.py.j2 +99 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_i2c_test.py.j2 +117 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_pinmux_test.py.j2 +43 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_spi_test.py.j2 +94 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_gpio_test.c.j2 +113 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_i2c_test.c.j2 +101 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_spi_test.c.j2 +94 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/unittest/test_schematic.py.j2 +172 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/__init__.py +36 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/device_tree.py +1187 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/hierarchical_analysis.py +211 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/netlist.py +320 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/parts_registry.py +142 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb.py +955 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb_layout.py +308 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pin_analysis.py +765 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/project.py +196 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic.py +319 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_editor.py +674 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_search.py +158 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/validation.py +866 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/visualization.py +225 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/file_handlers.py +65 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_cli.py +103 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_version.py +103 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/parts_registry.py +197 -0
- package/mcp_servers/kicad-mcp-server/tests/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/tests/examples/ESP32S3_TEST.md +219 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/README.md +65 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/example_pcb.kicad_pcb +177 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/example_schematic.kicad_sch +145 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/child.kicad_sch +24 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/root.kicad_sch +38 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_hierarchical_labels.py +195 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_kicad_cli.py +116 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_netlist_cache_path.py +29 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic.py +189 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic_hierarchy.py +69 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_visualization.py +136 -0
- package/mcp_servers/kicad-mcp-server/uv.lock +2873 -0
- package/mcp_servers/openscad-mcp-server/.dockerignore +9 -0
- package/mcp_servers/openscad-mcp-server/.github/workflows/test.yml +40 -0
- package/mcp_servers/openscad-mcp-server/Dockerfile +29 -0
- package/mcp_servers/openscad-mcp-server/LICENSE +21 -0
- package/mcp_servers/openscad-mcp-server/README.md +154 -0
- package/mcp_servers/openscad-mcp-server/docs/audit.md +56 -0
- package/mcp_servers/openscad-mcp-server/docs/docker.md +66 -0
- package/mcp_servers/openscad-mcp-server/docs/issue-followup.md +19 -0
- package/mcp_servers/openscad-mcp-server/docs/jetson.md +17 -0
- package/mcp_servers/openscad-mcp-server/glama.json +4 -0
- package/mcp_servers/openscad-mcp-server/legacy/README.md +15 -0
- package/mcp_servers/openscad-mcp-server/legacy/README.original.md +294 -0
- package/mcp_servers/openscad-mcp-server/legacy/implementation_plan.md +100 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/download_sam2_checkpoint.py +115 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/src/ai/sam_segmentation.py +209 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/src/models/threestudio_generator.py +231 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/src/workflow/image_to_model_pipeline.py +260 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/test_sam2_segmentation.py +96 -0
- package/mcp_servers/openscad-mcp-server/legacy/requirements.txt +57 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/README.md +39 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/ai-driven-code-generation.md +122 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/export-formats.md +76 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/ai/ai_service.py.md +51 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/main.py.md +63 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/models/code_generator.py.md +63 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/nlp/parameter_extractor.py.md +63 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/ai/natural-language-processing.md +78 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/nlp/parameter-extraction.md +173 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/export-formats.md +91 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/openscad-basics.md +66 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/primitive-testing.md +79 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/ai/ai_service.py +257 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/ai/gemini_api.py +161 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/ai/venice_api.py +203 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/config.py +121 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/main.py +1456 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/main.py.new +404 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/main_remote.py +401 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/code_generator.py +321 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/cuda_mvs.py +209 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/scad_templates/basic_shapes.scad +144 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/nlp/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/nlp/parameter_extractor.py +388 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/wrapper.py +418 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/__init__.py +1 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/printer_discovery.py +471 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/connection_manager.py +537 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_client.py +435 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_server.py +787 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/error_handling.py +415 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/testing/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/testing/primitive_tester.py +203 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/testing/test_primitives.py +98 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/__init__.py +1 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/cad_exporter.py +241 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/format_validator.py +206 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_exporter.py +140 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_repair.py +91 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_validator.py +123 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/headless_renderer.py +52 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/renderer.py +177 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/web_interface.py +639 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/workflow/image_approval.py +148 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/workflow/multi_view_to_model_pipeline.py +338 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_complete_workflow.py +374 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_cuda_mvs.py +191 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_gemini_api.py +168 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_image_approval.py +192 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_image_approval_workflow.py +251 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_image_to_model_pipeline.py +145 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_model_selection.py +41 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_multi_view_pipeline.py +290 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_primitives.sh +13 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_rabbit_direct.py +71 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_remote_cuda_mvs.py +283 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_venice_example.py +69 -0
- package/mcp_servers/openscad-mcp-server/pyproject.toml +33 -0
- package/mcp_servers/openscad-mcp-server/requirements.txt +2 -0
- package/mcp_servers/openscad-mcp-server/scad/simple_cube.scad +2 -0
- package/mcp_servers/openscad-mcp-server/scripts/test_docker.py +220 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__init__.py +3 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__main__.py +3 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/engine.py +94 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/geometry.py +242 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/server.py +245 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/service.py +190 -0
- package/mcp_servers/openscad-mcp-server/tests/conftest.py +17 -0
- package/mcp_servers/openscad-mcp-server/tests/test_engine.py +37 -0
- package/mcp_servers/openscad-mcp-server/tests/test_geometry.py +106 -0
- package/mcp_servers/openscad-mcp-server/tests/test_integration.py +172 -0
- package/mcp_servers/openscad-mcp-server/tests/test_transports.py +314 -0
- package/mcp_servers/openscad-mcp-server/uv.lock +1123 -0
- package/package.json +44 -0
- package/scripts/install_mcps.sh +86 -0
- package/scripts/openscad_wrapper.sh +62 -0
- package/scripts/run_kicad_mcp.sh +25 -0
- package/scripts/run_openscad_mcp.sh +41 -0
- package/scripts/test_mcp_connection.py +626 -0
- package/scripts/test_mcp_connection.sh +168 -0
- package/skills/code-first-hardware-design/SKILL.md +260 -0
- package/skills/pcb-constraint-definition/SKILL.md +236 -0
- package/skills/pcb-layout-routing-automation/SKILL.md +153 -0
- package/skills/pcb-validation-dfm-signoff/SKILL.md +194 -0
- package/skills/schematic-datasheet-analysis/SKILL.md +202 -0
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
import socket
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
import threading
|
|
7
|
+
from typing import Dict, List, Any, Optional, Callable
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
class PrinterDiscovery:
|
|
12
|
+
"""
|
|
13
|
+
Discovers 3D printers on the network and provides interfaces for direct printing.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
"""Initialize the printer discovery service."""
|
|
18
|
+
self.printers = {} # Dictionary of discovered printers
|
|
19
|
+
self.discovery_thread = None
|
|
20
|
+
self.discovery_stop_event = threading.Event()
|
|
21
|
+
self.discovery_callback = None
|
|
22
|
+
|
|
23
|
+
def start_discovery(self, callback: Optional[Callable[[Dict[str, Any]], None]] = None) -> None:
|
|
24
|
+
"""
|
|
25
|
+
Start discovering 3D printers on the network.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
callback: Optional callback function to call when a printer is discovered
|
|
29
|
+
"""
|
|
30
|
+
if self.discovery_thread and self.discovery_thread.is_alive():
|
|
31
|
+
logger.warning("Printer discovery already running")
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
self.discovery_callback = callback
|
|
35
|
+
self.discovery_stop_event.clear()
|
|
36
|
+
self.discovery_thread = threading.Thread(target=self._discover_printers)
|
|
37
|
+
self.discovery_thread.daemon = True
|
|
38
|
+
self.discovery_thread.start()
|
|
39
|
+
|
|
40
|
+
logger.info("Started printer discovery")
|
|
41
|
+
|
|
42
|
+
def stop_discovery(self) -> None:
|
|
43
|
+
"""Stop discovering 3D printers."""
|
|
44
|
+
if self.discovery_thread and self.discovery_thread.is_alive():
|
|
45
|
+
self.discovery_stop_event.set()
|
|
46
|
+
self.discovery_thread.join(timeout=2.0)
|
|
47
|
+
logger.info("Stopped printer discovery")
|
|
48
|
+
else:
|
|
49
|
+
logger.warning("Printer discovery not running")
|
|
50
|
+
|
|
51
|
+
def get_printers(self) -> Dict[str, Any]:
|
|
52
|
+
"""
|
|
53
|
+
Get the list of discovered printers.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Dictionary of printer information
|
|
57
|
+
"""
|
|
58
|
+
return self.printers
|
|
59
|
+
|
|
60
|
+
def _discover_printers(self) -> None:
|
|
61
|
+
"""Discover 3D printers on the network using various protocols."""
|
|
62
|
+
# This is a simplified implementation that simulates printer discovery
|
|
63
|
+
# In a real implementation, you would use protocols like mDNS, SNMP, or OctoPrint API
|
|
64
|
+
|
|
65
|
+
# Simulate discovering printers
|
|
66
|
+
while not self.discovery_stop_event.is_set():
|
|
67
|
+
try:
|
|
68
|
+
# Simulate network discovery
|
|
69
|
+
self._discover_octoprint_printers()
|
|
70
|
+
self._discover_prusa_printers()
|
|
71
|
+
self._discover_ultimaker_printers()
|
|
72
|
+
|
|
73
|
+
# Wait before next discovery cycle
|
|
74
|
+
time.sleep(10)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.error(f"Error in printer discovery: {str(e)}")
|
|
77
|
+
time.sleep(5)
|
|
78
|
+
|
|
79
|
+
def _discover_octoprint_printers(self) -> None:
|
|
80
|
+
"""Discover OctoPrint servers on the network."""
|
|
81
|
+
# Simulate discovering OctoPrint servers
|
|
82
|
+
# In a real implementation, you would use mDNS to discover OctoPrint instances
|
|
83
|
+
|
|
84
|
+
# Simulate finding a printer
|
|
85
|
+
printer_id = "octoprint_1"
|
|
86
|
+
if printer_id not in self.printers:
|
|
87
|
+
printer_info = {
|
|
88
|
+
"id": printer_id,
|
|
89
|
+
"name": "OctoPrint Printer",
|
|
90
|
+
"type": "octoprint",
|
|
91
|
+
"address": "192.168.1.100",
|
|
92
|
+
"port": 80,
|
|
93
|
+
"api_key": None, # Would need to be provided by user
|
|
94
|
+
"status": "online",
|
|
95
|
+
"capabilities": ["print", "status", "cancel"]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
self.printers[printer_id] = printer_info
|
|
99
|
+
|
|
100
|
+
if self.discovery_callback:
|
|
101
|
+
self.discovery_callback(printer_info)
|
|
102
|
+
|
|
103
|
+
logger.info(f"Discovered OctoPrint printer: {printer_info['name']}")
|
|
104
|
+
|
|
105
|
+
def _discover_prusa_printers(self) -> None:
|
|
106
|
+
"""Discover Prusa printers on the network."""
|
|
107
|
+
# Simulate discovering Prusa printers
|
|
108
|
+
|
|
109
|
+
# Simulate finding a printer
|
|
110
|
+
printer_id = "prusa_1"
|
|
111
|
+
if printer_id not in self.printers:
|
|
112
|
+
printer_info = {
|
|
113
|
+
"id": printer_id,
|
|
114
|
+
"name": "Prusa MK3S",
|
|
115
|
+
"type": "prusa",
|
|
116
|
+
"address": "192.168.1.101",
|
|
117
|
+
"port": 80,
|
|
118
|
+
"status": "online",
|
|
119
|
+
"capabilities": ["print", "status"]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
self.printers[printer_id] = printer_info
|
|
123
|
+
|
|
124
|
+
if self.discovery_callback:
|
|
125
|
+
self.discovery_callback(printer_info)
|
|
126
|
+
|
|
127
|
+
logger.info(f"Discovered Prusa printer: {printer_info['name']}")
|
|
128
|
+
|
|
129
|
+
def _discover_ultimaker_printers(self) -> None:
|
|
130
|
+
"""Discover Ultimaker printers on the network."""
|
|
131
|
+
# Simulate discovering Ultimaker printers
|
|
132
|
+
|
|
133
|
+
# Simulate finding a printer
|
|
134
|
+
printer_id = "ultimaker_1"
|
|
135
|
+
if printer_id not in self.printers:
|
|
136
|
+
printer_info = {
|
|
137
|
+
"id": printer_id,
|
|
138
|
+
"name": "Ultimaker S5",
|
|
139
|
+
"type": "ultimaker",
|
|
140
|
+
"address": "192.168.1.102",
|
|
141
|
+
"port": 80,
|
|
142
|
+
"status": "online",
|
|
143
|
+
"capabilities": ["print", "status", "cancel"]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
self.printers[printer_id] = printer_info
|
|
147
|
+
|
|
148
|
+
if self.discovery_callback:
|
|
149
|
+
self.discovery_callback(printer_info)
|
|
150
|
+
|
|
151
|
+
logger.info(f"Discovered Ultimaker printer: {printer_info['name']}")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class PrinterInterface:
|
|
155
|
+
"""
|
|
156
|
+
Interface for communicating with 3D printers.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
def __init__(self, printer_discovery: PrinterDiscovery):
|
|
160
|
+
"""
|
|
161
|
+
Initialize the printer interface.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
printer_discovery: Instance of PrinterDiscovery for finding printers
|
|
165
|
+
"""
|
|
166
|
+
self.printer_discovery = printer_discovery
|
|
167
|
+
self.connected_printers = {} # Dictionary of connected printers
|
|
168
|
+
|
|
169
|
+
def connect_to_printer(self, printer_id: str, credentials: Optional[Dict[str, Any]] = None) -> bool:
|
|
170
|
+
"""
|
|
171
|
+
Connect to a specific printer.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
printer_id: ID of the printer to connect to
|
|
175
|
+
credentials: Optional credentials for authentication
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
True if connection successful, False otherwise
|
|
179
|
+
"""
|
|
180
|
+
printers = self.printer_discovery.get_printers()
|
|
181
|
+
if printer_id not in printers:
|
|
182
|
+
logger.error(f"Printer not found: {printer_id}")
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
printer_info = printers[printer_id]
|
|
186
|
+
|
|
187
|
+
# Create appropriate printer client based on type
|
|
188
|
+
if printer_info["type"] == "octoprint":
|
|
189
|
+
client = OctoPrintClient(printer_info, credentials)
|
|
190
|
+
elif printer_info["type"] == "prusa":
|
|
191
|
+
client = PrusaClient(printer_info, credentials)
|
|
192
|
+
elif printer_info["type"] == "ultimaker":
|
|
193
|
+
client = UltimakerClient(printer_info, credentials)
|
|
194
|
+
else:
|
|
195
|
+
logger.error(f"Unsupported printer type: {printer_info['type']}")
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
# Connect to the printer
|
|
199
|
+
if client.connect():
|
|
200
|
+
self.connected_printers[printer_id] = client
|
|
201
|
+
return True
|
|
202
|
+
else:
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
def disconnect_from_printer(self, printer_id: str) -> bool:
|
|
206
|
+
"""
|
|
207
|
+
Disconnect from a specific printer.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
printer_id: ID of the printer to disconnect from
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
True if disconnection successful, False otherwise
|
|
214
|
+
"""
|
|
215
|
+
if printer_id not in self.connected_printers:
|
|
216
|
+
logger.error(f"Not connected to printer: {printer_id}")
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
client = self.connected_printers[printer_id]
|
|
220
|
+
if client.disconnect():
|
|
221
|
+
del self.connected_printers[printer_id]
|
|
222
|
+
return True
|
|
223
|
+
else:
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
def print_file(self, printer_id: str, file_path: str, print_settings: Optional[Dict[str, Any]] = None) -> bool:
|
|
227
|
+
"""
|
|
228
|
+
Send a file to a printer for printing.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
printer_id: ID of the printer to print on
|
|
232
|
+
file_path: Path to the STL file to print
|
|
233
|
+
print_settings: Optional print settings
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
True if print job started successfully, False otherwise
|
|
237
|
+
"""
|
|
238
|
+
if printer_id not in self.connected_printers:
|
|
239
|
+
logger.error(f"Not connected to printer: {printer_id}")
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
client = self.connected_printers[printer_id]
|
|
243
|
+
return client.print_file(file_path, print_settings)
|
|
244
|
+
|
|
245
|
+
def get_printer_status(self, printer_id: str) -> Dict[str, Any]:
|
|
246
|
+
"""
|
|
247
|
+
Get the status of a specific printer.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
printer_id: ID of the printer to get status for
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Dictionary with printer status information
|
|
254
|
+
"""
|
|
255
|
+
if printer_id not in self.connected_printers:
|
|
256
|
+
logger.error(f"Not connected to printer: {printer_id}")
|
|
257
|
+
return {"error": "Not connected to printer"}
|
|
258
|
+
|
|
259
|
+
client = self.connected_printers[printer_id]
|
|
260
|
+
return client.get_status()
|
|
261
|
+
|
|
262
|
+
def cancel_print(self, printer_id: str) -> bool:
|
|
263
|
+
"""
|
|
264
|
+
Cancel a print job on a specific printer.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
printer_id: ID of the printer to cancel print on
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
True if cancellation successful, False otherwise
|
|
271
|
+
"""
|
|
272
|
+
if printer_id not in self.connected_printers:
|
|
273
|
+
logger.error(f"Not connected to printer: {printer_id}")
|
|
274
|
+
return False
|
|
275
|
+
|
|
276
|
+
client = self.connected_printers[printer_id]
|
|
277
|
+
return client.cancel_print()
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class PrinterClient:
|
|
281
|
+
"""Base class for printer clients."""
|
|
282
|
+
|
|
283
|
+
def __init__(self, printer_info: Dict[str, Any], credentials: Optional[Dict[str, Any]] = None):
|
|
284
|
+
"""
|
|
285
|
+
Initialize the printer client.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
printer_info: Information about the printer
|
|
289
|
+
credentials: Optional credentials for authentication
|
|
290
|
+
"""
|
|
291
|
+
self.printer_info = printer_info
|
|
292
|
+
self.credentials = credentials or {}
|
|
293
|
+
self.connected = False
|
|
294
|
+
|
|
295
|
+
def connect(self) -> bool:
|
|
296
|
+
"""
|
|
297
|
+
Connect to the printer.
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
True if connection successful, False otherwise
|
|
301
|
+
"""
|
|
302
|
+
# Base implementation - should be overridden by subclasses
|
|
303
|
+
self.connected = True
|
|
304
|
+
return True
|
|
305
|
+
|
|
306
|
+
def disconnect(self) -> bool:
|
|
307
|
+
"""
|
|
308
|
+
Disconnect from the printer.
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
True if disconnection successful, False otherwise
|
|
312
|
+
"""
|
|
313
|
+
# Base implementation - should be overridden by subclasses
|
|
314
|
+
self.connected = False
|
|
315
|
+
return True
|
|
316
|
+
|
|
317
|
+
def print_file(self, file_path: str, print_settings: Optional[Dict[str, Any]] = None) -> bool:
|
|
318
|
+
"""
|
|
319
|
+
Send a file to the printer for printing.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
file_path: Path to the STL file to print
|
|
323
|
+
print_settings: Optional print settings
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
True if print job started successfully, False otherwise
|
|
327
|
+
"""
|
|
328
|
+
# Base implementation - should be overridden by subclasses
|
|
329
|
+
if not self.connected:
|
|
330
|
+
logger.error("Not connected to printer")
|
|
331
|
+
return False
|
|
332
|
+
|
|
333
|
+
logger.info(f"Printing file: {file_path}")
|
|
334
|
+
return True
|
|
335
|
+
|
|
336
|
+
def get_status(self) -> Dict[str, Any]:
|
|
337
|
+
"""
|
|
338
|
+
Get the status of the printer.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Dictionary with printer status information
|
|
342
|
+
"""
|
|
343
|
+
# Base implementation - should be overridden by subclasses
|
|
344
|
+
if not self.connected:
|
|
345
|
+
return {"status": "disconnected"}
|
|
346
|
+
|
|
347
|
+
return {"status": "connected"}
|
|
348
|
+
|
|
349
|
+
def cancel_print(self) -> bool:
|
|
350
|
+
"""
|
|
351
|
+
Cancel the current print job.
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
True if cancellation successful, False otherwise
|
|
355
|
+
"""
|
|
356
|
+
# Base implementation - should be overridden by subclasses
|
|
357
|
+
if not self.connected:
|
|
358
|
+
logger.error("Not connected to printer")
|
|
359
|
+
return False
|
|
360
|
+
|
|
361
|
+
logger.info("Cancelling print job")
|
|
362
|
+
return True
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class OctoPrintClient(PrinterClient):
|
|
366
|
+
"""Client for OctoPrint printers."""
|
|
367
|
+
|
|
368
|
+
def connect(self) -> bool:
|
|
369
|
+
"""Connect to an OctoPrint server."""
|
|
370
|
+
try:
|
|
371
|
+
# In a real implementation, you would use the OctoPrint API
|
|
372
|
+
# to connect to the printer
|
|
373
|
+
|
|
374
|
+
# Check if API key is provided
|
|
375
|
+
if "api_key" not in self.credentials:
|
|
376
|
+
logger.error("API key required for OctoPrint")
|
|
377
|
+
return False
|
|
378
|
+
|
|
379
|
+
# Simulate connection
|
|
380
|
+
logger.info(f"Connected to OctoPrint server: {self.printer_info['address']}")
|
|
381
|
+
self.connected = True
|
|
382
|
+
return True
|
|
383
|
+
except Exception as e:
|
|
384
|
+
logger.error(f"Error connecting to OctoPrint server: {str(e)}")
|
|
385
|
+
return False
|
|
386
|
+
|
|
387
|
+
def print_file(self, file_path: str, print_settings: Optional[Dict[str, Any]] = None) -> bool:
|
|
388
|
+
"""Send a file to an OctoPrint server for printing."""
|
|
389
|
+
if not self.connected:
|
|
390
|
+
logger.error("Not connected to OctoPrint server")
|
|
391
|
+
return False
|
|
392
|
+
|
|
393
|
+
try:
|
|
394
|
+
# In a real implementation, you would use the OctoPrint API
|
|
395
|
+
# to upload the file and start printing
|
|
396
|
+
|
|
397
|
+
# Check if file exists
|
|
398
|
+
if not os.path.exists(file_path):
|
|
399
|
+
logger.error(f"File not found: {file_path}")
|
|
400
|
+
return False
|
|
401
|
+
|
|
402
|
+
# Simulate printing
|
|
403
|
+
logger.info(f"Printing file on OctoPrint server: {file_path}")
|
|
404
|
+
return True
|
|
405
|
+
except Exception as e:
|
|
406
|
+
logger.error(f"Error printing file on OctoPrint server: {str(e)}")
|
|
407
|
+
return False
|
|
408
|
+
|
|
409
|
+
def get_status(self) -> Dict[str, Any]:
|
|
410
|
+
"""Get the status of an OctoPrint server."""
|
|
411
|
+
if not self.connected:
|
|
412
|
+
return {"status": "disconnected"}
|
|
413
|
+
|
|
414
|
+
try:
|
|
415
|
+
# In a real implementation, you would use the OctoPrint API
|
|
416
|
+
# to get the printer status
|
|
417
|
+
|
|
418
|
+
# Simulate status
|
|
419
|
+
return {
|
|
420
|
+
"status": "connected",
|
|
421
|
+
"printer": {
|
|
422
|
+
"state": "operational",
|
|
423
|
+
"temperature": {
|
|
424
|
+
"bed": {"actual": 60.0, "target": 60.0},
|
|
425
|
+
"tool0": {"actual": 210.0, "target": 210.0}
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
"job": {
|
|
429
|
+
"file": {"name": "example.gcode"},
|
|
430
|
+
"progress": {"completion": 0.0, "printTime": 0, "printTimeLeft": 0}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
except Exception as e:
|
|
434
|
+
logger.error(f"Error getting status from OctoPrint server: {str(e)}")
|
|
435
|
+
return {"status": "error", "message": str(e)}
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
class PrusaClient(PrinterClient):
|
|
439
|
+
"""Client for Prusa printers."""
|
|
440
|
+
|
|
441
|
+
def connect(self) -> bool:
|
|
442
|
+
"""Connect to a Prusa printer."""
|
|
443
|
+
try:
|
|
444
|
+
# In a real implementation, you would use the Prusa API
|
|
445
|
+
# to connect to the printer
|
|
446
|
+
|
|
447
|
+
# Simulate connection
|
|
448
|
+
logger.info(f"Connected to Prusa printer: {self.printer_info['address']}")
|
|
449
|
+
self.connected = True
|
|
450
|
+
return True
|
|
451
|
+
except Exception as e:
|
|
452
|
+
logger.error(f"Error connecting to Prusa printer: {str(e)}")
|
|
453
|
+
return False
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
class UltimakerClient(PrinterClient):
|
|
457
|
+
"""Client for Ultimaker printers."""
|
|
458
|
+
|
|
459
|
+
def connect(self) -> bool:
|
|
460
|
+
"""Connect to an Ultimaker printer."""
|
|
461
|
+
try:
|
|
462
|
+
# In a real implementation, you would use the Ultimaker API
|
|
463
|
+
# to connect to the printer
|
|
464
|
+
|
|
465
|
+
# Simulate connection
|
|
466
|
+
logger.info(f"Connected to Ultimaker printer: {self.printer_info['address']}")
|
|
467
|
+
self.connected = True
|
|
468
|
+
return True
|
|
469
|
+
except Exception as e:
|
|
470
|
+
logger.error(f"Error connecting to Ultimaker printer: {str(e)}")
|
|
471
|
+
return False
|