|
57 | 57 | from lib.canopen_sdo import SDOTimeoutError, SDOAbortError |
58 | 58 | CAN_AVAILABLE = True |
59 | 59 | except ImportError as e: |
60 | | - print(f"[OI] Warning: CAN/SDO modules not available - {e}") |
61 | | - print("[OI] Using demo data only. To enable device support, upload lib/canopen_sdo.py") |
| 60 | + # Don't print - it interferes with M2M JSON responses in silent mode |
62 | 61 | CAN_AVAILABLE = False |
63 | 62 | # Define dummy exception classes so code doesn't break |
64 | 63 | class SDOTimeoutError(Exception): |
@@ -123,13 +122,16 @@ class SDOAbortError(Exception): |
123 | 122 | def _send_response(cmd, arg): |
124 | 123 | """Internal helper to send JSON response to WebREPL client""" |
125 | 124 | response = json.dumps({'CMD': cmd, 'ARG': arg}) |
126 | | - webrepl.send(response) |
| 125 | + result = webrepl.send(response) |
| 126 | + # Debug: print returns True/False indicating if send succeeded |
| 127 | + print(f"[OI_DEBUG] _send_response: cmd={cmd}, sent={result}, len={len(response)}") |
127 | 128 |
|
128 | 129 |
|
129 | 130 | def _send_error(message, cmd): |
130 | 131 | """Internal helper to send error response""" |
131 | 132 | response = json.dumps({'CMD': cmd, 'ARG': {'error': message}}) |
132 | | - webrepl.send(response) |
| 133 | + result = webrepl.send(response) |
| 134 | + print(f"[OI_DEBUG] _send_error: cmd={cmd}, sent={result}, msg={message}") |
133 | 135 |
|
134 | 136 |
|
135 | 137 | def _send_success(message, cmd): |
@@ -1671,103 +1673,99 @@ def scanCanBus(args=None): |
1671 | 1673 | |
1672 | 1674 | Returns list of detected nodes with their SDO responses. |
1673 | 1675 | """ |
1674 | | - global can_dev |
1675 | | - |
1676 | | - if not CAN_AVAILABLE: |
1677 | | - _send_error("CAN module not available", 'CAN-SCAN-ERROR') |
1678 | | - return |
1679 | | - |
1680 | | - # Initialize CAN if not already initialized |
1681 | | - if can_dev is None: |
1682 | | - if args is None: |
1683 | | - args = {} |
1684 | | - tx_pin = args.get('tx_pin', 5) |
1685 | | - rx_pin = args.get('rx_pin', 4) |
1686 | | - bitrate = args.get('bitrate', 500000) |
| 1676 | + try: |
| 1677 | + global can_dev |
1687 | 1678 |
|
1688 | | - try: |
1689 | | - print(f"[OI] Auto-initializing CAN for scan: tx={tx_pin}, rx={rx_pin}, bitrate={bitrate}") |
1690 | | - can_dev = CAN(0, extframe=False, tx=tx_pin, rx=rx_pin, mode=CAN.NORMAL, bitrate=bitrate, auto_restart=False) |
1691 | | - print("[OI] CAN initialized successfully") |
1692 | | - except Exception as e: |
1693 | | - print(f"[OI] Failed to initialize CAN: {e}") |
1694 | | - _send_error(f"Failed to initialize CAN: {e}", 'CAN-SCAN-ERROR') |
| 1679 | + if not CAN_AVAILABLE: |
| 1680 | + _send_error("CAN module not available", 'CAN-SCAN-ERROR') |
1695 | 1681 | return |
1696 | | - |
1697 | | - # Default to quick scan (nodes 1-10) for better UX |
1698 | | - quick_scan = args.get('quick', True) if args else True |
1699 | | - default_range = range(1, 11) if quick_scan else range(1, 128) |
1700 | | - node_ids = args.get('node_ids', list(default_range)) if args else list(default_range) |
1701 | | - timeout = args.get('timeout', 0.1) if args else 0.1 |
1702 | | - |
1703 | | - total_nodes = len(node_ids) |
1704 | | - print(f"[OI] Scanning CAN bus for {total_nodes} node IDs (timeout: {timeout*1000}ms per node)") |
1705 | | - |
1706 | | - found_nodes = [] |
1707 | | - |
1708 | | - for index, node_id in enumerate(node_ids): |
1709 | | - # Don't send progress updates during execute() as they interfere with JSON parsing |
1710 | | - # Progress updates would need to be handled differently (via streaming/websocket) |
1711 | | - # if index % 10 == 0 or index == 0: |
1712 | | - # progress = int((index / total_nodes) * 100) |
1713 | | - # _send_response('CAN-SCAN-PROGRESS', { |
1714 | | - # 'progress': progress, |
1715 | | - # 'current': node_id, |
1716 | | - # 'total': total_nodes, |
1717 | | - # 'found': len(found_nodes) |
1718 | | - # }) |
1719 | 1682 |
|
1720 | | - try: |
1721 | | - # Create temporary SDO client for this node |
1722 | | - temp_sdo = SDOClient(can_dev, node_id=node_id, timeout=timeout) |
| 1683 | + # Initialize CAN if not already initialized |
| 1684 | + if can_dev is None: |
| 1685 | + if args is None: |
| 1686 | + args = {} |
| 1687 | + tx_pin = args.get('tx_pin', 5) |
| 1688 | + rx_pin = args.get('rx_pin', 4) |
| 1689 | + bitrate = args.get('bitrate', 500000) |
1723 | 1690 |
|
1724 | | - # Try to read a standard parameter (e.g., index 0x1000 - device type) |
1725 | | - # This is a CANopen standard object that should exist |
1726 | 1691 | try: |
1727 | | - device_type = temp_sdo.read(0x1000, 0) |
1728 | | - |
1729 | | - # Try to read serial number too |
1730 | | - serial_number = None |
| 1692 | + can_dev = CAN(0, extframe=False, tx=tx_pin, rx=rx_pin, mode=CAN.NORMAL, bitrate=bitrate, auto_restart=False) |
| 1693 | + except Exception as e: |
| 1694 | + _send_error(f"Failed to initialize CAN: {e}", 'CAN-SCAN-ERROR') |
| 1695 | + return |
| 1696 | + |
| 1697 | + # Default to quick scan (nodes 1-10) for better UX |
| 1698 | + quick_scan = args.get('quick', True) if args else True |
| 1699 | + default_range = range(1, 11) if quick_scan else range(1, 128) |
| 1700 | + node_ids = args.get('node_ids', list(default_range)) if args else list(default_range) |
| 1701 | + timeout = args.get('timeout', 0.1) if args else 0.1 |
| 1702 | + |
| 1703 | + total_nodes = len(node_ids) |
| 1704 | + found_nodes = [] |
| 1705 | + |
| 1706 | + for index, node_id in enumerate(node_ids): |
| 1707 | + # Don't send progress updates during execute() as they interfere with JSON parsing |
| 1708 | + # Progress updates would need to be handled differently (via streaming/websocket) |
| 1709 | + # if index % 10 == 0 or index == 0: |
| 1710 | + # progress = int((index / total_nodes) * 100) |
| 1711 | + # _send_response('CAN-SCAN-PROGRESS', { |
| 1712 | + # 'progress': progress, |
| 1713 | + # 'current': node_id, |
| 1714 | + # 'total': total_nodes, |
| 1715 | + # 'found': len(found_nodes) |
| 1716 | + # }) |
| 1717 | + |
| 1718 | + try: |
| 1719 | + # Create temporary SDO client for this node |
| 1720 | + temp_sdo = SDOClient(can_dev, node_id=node_id, timeout=timeout) |
| 1721 | + |
| 1722 | + # Try to read a standard parameter (e.g., index 0x1000 - device type) |
| 1723 | + # This is a CANopen standard object that should exist |
1731 | 1724 | try: |
1732 | | - # Try reading from OpenInverter serial number location |
1733 | | - serial_raw = temp_sdo.read(0x5000, 0) |
1734 | | - serial_number = f"{serial_raw:08X}" |
1735 | | - except: |
| 1725 | + device_type = temp_sdo.read(0x1000, 0) |
| 1726 | + |
| 1727 | + # Try to read serial number too |
| 1728 | + serial_number = None |
| 1729 | + try: |
| 1730 | + # Try reading from OpenInverter serial number location |
| 1731 | + serial_raw = temp_sdo.read(0x5000, 0) |
| 1732 | + serial_number = f"{serial_raw:08X}" |
| 1733 | + except: |
| 1734 | + pass |
| 1735 | + |
| 1736 | + # Node responded, add to list |
| 1737 | + found_nodes.append({ |
| 1738 | + 'nodeId': node_id, |
| 1739 | + 'serialNumber': serial_number, |
| 1740 | + 'deviceType': device_type, |
| 1741 | + 'responding': True |
| 1742 | + }) |
| 1743 | + |
| 1744 | + # Don't send progress updates during execute() as they interfere with JSON parsing |
| 1745 | + # progress = int(((index + 1) / total_nodes) * 100) |
| 1746 | + # _send_response('CAN-SCAN-PROGRESS', { |
| 1747 | + # 'progress': progress, |
| 1748 | + # 'current': node_id, |
| 1749 | + # 'total': total_nodes, |
| 1750 | + # 'found': len(found_nodes), |
| 1751 | + # 'lastFound': node_id |
| 1752 | + # }) |
| 1753 | + |
| 1754 | + except (SDOTimeoutError, SDOAbortError): |
| 1755 | + # Node didn't respond or doesn't have this object |
1736 | 1756 | pass |
1737 | | - |
1738 | | - # Node responded, add to list |
1739 | | - found_nodes.append({ |
1740 | | - 'nodeId': node_id, |
1741 | | - 'serialNumber': serial_number, |
1742 | | - 'deviceType': device_type, |
1743 | | - 'responding': True |
1744 | | - }) |
1745 | | - |
1746 | | - print(f"[OI] Found node {node_id} (device type: 0x{device_type:08X}, serial: {serial_number})") |
1747 | | - |
1748 | | - # Don't send progress updates during execute() as they interfere with JSON parsing |
1749 | | - # progress = int(((index + 1) / total_nodes) * 100) |
1750 | | - # _send_response('CAN-SCAN-PROGRESS', { |
1751 | | - # 'progress': progress, |
1752 | | - # 'current': node_id, |
1753 | | - # 'total': total_nodes, |
1754 | | - # 'found': len(found_nodes), |
1755 | | - # 'lastFound': node_id |
1756 | | - # }) |
1757 | | - |
1758 | | - except (SDOTimeoutError, SDOAbortError): |
1759 | | - # Node didn't respond or doesn't have this object |
1760 | | - pass |
1761 | | - |
1762 | | - except Exception as e: |
1763 | | - print(f"[OI] Error scanning node {node_id}: {e}") |
1764 | | - |
1765 | | - # Send final result |
1766 | | - scan_type = "quick" if quick_scan else "full" |
1767 | | - print(f"[OI] {scan_type.capitalize()} scan complete. Found {len(found_nodes)} device(s).") |
1768 | | - _send_response('CAN-SCAN-RESULT', { |
1769 | | - 'devices': found_nodes, |
1770 | | - 'scanned': total_nodes, |
1771 | | - 'scanType': scan_type |
1772 | | - }) |
| 1757 | + |
| 1758 | + except Exception as e: |
| 1759 | + pass # Ignore errors for individual nodes |
| 1760 | + |
| 1761 | + # Send final result |
| 1762 | + scan_type = "quick" if quick_scan else "full" |
| 1763 | + _send_response('CAN-SCAN-RESULT', { |
| 1764 | + 'devices': found_nodes, |
| 1765 | + 'scanned': total_nodes, |
| 1766 | + 'scanType': scan_type |
| 1767 | + }) |
| 1768 | + except Exception as e: |
| 1769 | + # Send error via M2M channel (no print to avoid stdout interference) |
| 1770 | + _send_error(f"Scan failed: {str(e)}", 'CAN-SCAN-ERROR') |
1773 | 1771 |
|
0 commit comments