Georg commited on
Commit
18ba7d2
·
1 Parent(s): 598c3a8

Enhance Nova API state management in frontend

Browse files

- Introduced a new state tracking mechanism for the Nova API to minimize unnecessary UI updates.
- Updated connection handling to only refresh UI elements when there are changes in the Nova API state.
- Improved error detection and notification for Nova connection issues, providing clearer feedback to users.
- Refactored badge visibility and controller indicators to reflect the current Nova API state more accurately.
- Enhanced mode text updates based on connection status, ensuring users receive timely information about the system's state.

Files changed (1) hide show
  1. frontend/index.html +120 -67
frontend/index.html CHANGED
@@ -1118,6 +1118,14 @@
1118
  let currentHomePose = null; // Store home pose from env data
1119
  let currentJointPositions = null; // Store latest joint positions from state stream
1120
  let prevNovaConnected = null; // Track previous Nova connection state for error detection
 
 
 
 
 
 
 
 
1121
  function updateConnectionLabel(text) {
1122
  if (connStatusText) {
1123
  connStatusText.innerText = text;
@@ -1510,15 +1518,25 @@
1510
  fetch('/nova-sim/api/v1/env')
1511
  .then(r => r.json())
1512
  .then(envData => {
 
1513
  has_gripper = envData.has_gripper || false;
1514
  control_mode = envData.control_mode || 'ik';
1515
  if (envData.home_pose) {
1516
  currentHomePose = envData.home_pose;
1517
  }
 
 
1518
  });
1519
  }
1520
  if (data.scene && data.scene !== currentScene) {
1521
  currentScene = data.scene;
 
 
 
 
 
 
 
1522
  }
1523
 
1524
  // Handle both new and legacy client status formats
@@ -1622,84 +1640,119 @@
1622
  }
1623
  }
1624
 
1625
- // Update Nova API controller status
1626
  if (data.nova_api) {
1627
- const stateController = document.getElementById('nova_state_controller');
1628
- const ikController = document.getElementById('nova_ik_controller');
1629
- const badge = document.getElementById('nova_connection_badge');
1630
- const badgeTitle = document.getElementById('nova_badge_title');
1631
- const modeText = document.getElementById('nova_mode_text');
1632
- setNovaToggleState(data.nova_api.available, data.nova_api.enabled);
1633
-
1634
- // Track Nova state streaming status
1635
- novaStateStreaming = data.nova_api.state_streaming || false;
1636
-
1637
- // Detect Nova connection errors and show toast notifications
1638
- const currentNovaConnected = data.nova_api.connected;
1639
- if (prevNovaConnected !== null && prevNovaConnected !== currentNovaConnected) {
1640
- if (!currentNovaConnected && data.nova_api.enabled) {
1641
- // Connection was lost or failed to connect while Nova is enabled
1642
- showClientNotification({
1643
- message: 'Nova API connection lost. Check that Nova is running and accessible.',
1644
- status: 'error'
1645
- });
1646
- } else if (currentNovaConnected && data.nova_api.enabled) {
1647
- // Connection was restored
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1648
  showClientNotification({
1649
- message: 'Nova API connection established.',
1650
- status: 'info'
1651
  });
1652
  }
1653
- }
1654
- // Also check for enabled but not connected state (initial error)
1655
- if (prevNovaConnected === null && data.nova_api.enabled && !currentNovaConnected) {
1656
- showClientNotification({
1657
- message: 'Nova API is enabled but not connected. Waiting for Nova to be ready...',
1658
- status: 'warning'
1659
- });
1660
- }
1661
- prevNovaConnected = currentNovaConnected;
1662
 
1663
- if (data.nova_api.available) {
1664
- badge.style.display = 'block';
1665
- } else {
1666
- badge.style.display = 'none';
1667
- }
1668
- badge.classList.toggle('connected', data.nova_api.enabled);
1669
- badge.classList.toggle('disconnected', !data.nova_api.enabled);
 
 
 
1670
 
1671
- const connectedColor = data.nova_api.connected ? 'var(--wb-success)' : 'var(--wb-highlight)';
1672
- if (stateController) {
1673
- stateController.innerText = data.nova_api.state_streaming ? 'Nova API' : 'Internal';
1674
- stateController.style.color = connectedColor;
1675
- }
1676
- if (ikController) {
1677
- ikController.innerText = data.nova_api.ik ? 'Nova API' : 'Internal';
1678
- ikController.style.color = data.nova_api.ik ? 'var(--wb-success)' : 'var(--wb-highlight)';
1679
- }
 
1680
 
1681
- if (badgeTitle) {
1682
- if (data.nova_api.enabled) {
1683
- badgeTitle.innerText = data.nova_api.connected ? '🌐 Nova Connected' : '🌐 Nova Powering Up';
1684
- } else {
1685
- badgeTitle.innerText = '🌐 Nova Disabled';
 
 
1686
  }
1687
- }
1688
 
1689
- if (modeText) {
1690
- if (data.nova_api.connected) {
1691
- if (data.nova_api.state_streaming && data.nova_api.ik) {
1692
- modeText.innerText = 'Hybrid Mode';
1693
- } else if (data.nova_api.state_streaming) {
1694
- modeText.innerText = 'Digital Twin Mode';
1695
- } else if (data.nova_api.ik) {
1696
- modeText.innerText = 'Nova IK Mode';
 
 
 
 
 
 
 
 
 
 
1697
  }
1698
- } else if (data.nova_api.enabled) {
1699
- modeText.innerText = 'Awaiting connection';
1700
- } else {
1701
- modeText.innerText = 'Internal control';
1702
  }
 
 
 
 
 
 
 
 
 
1703
  }
1704
  }
1705
 
 
1118
  let currentHomePose = null; // Store home pose from env data
1119
  let currentJointPositions = null; // Store latest joint positions from state stream
1120
  let prevNovaConnected = null; // Track previous Nova connection state for error detection
1121
+ // Track previous Nova API state to avoid unnecessary UI updates
1122
+ let prevNovaState = {
1123
+ available: null,
1124
+ enabled: null,
1125
+ connected: null,
1126
+ state_streaming: null,
1127
+ ik: null
1128
+ };
1129
  function updateConnectionLabel(text) {
1130
  if (connStatusText) {
1131
  connStatusText.innerText = text;
 
1518
  fetch('/nova-sim/api/v1/env')
1519
  .then(r => r.json())
1520
  .then(envData => {
1521
+ envCache = envData;
1522
  has_gripper = envData.has_gripper || false;
1523
  control_mode = envData.control_mode || 'ik';
1524
  if (envData.home_pose) {
1525
  currentHomePose = envData.home_pose;
1526
  }
1527
+ // Update overlay tiles when robot changes
1528
+ refreshOverlayTiles();
1529
  });
1530
  }
1531
  if (data.scene && data.scene !== currentScene) {
1532
  currentScene = data.scene;
1533
+ // Update overlay tiles when scene changes (may have different camera feeds)
1534
+ fetch('/nova-sim/api/v1/env')
1535
+ .then(r => r.json())
1536
+ .then(envData => {
1537
+ envCache = envData;
1538
+ refreshOverlayTiles();
1539
+ });
1540
  }
1541
 
1542
  // Handle both new and legacy client status formats
 
1640
  }
1641
  }
1642
 
1643
+ // Update Nova API controller status (only when values change)
1644
  if (data.nova_api) {
1645
+ const novaApi = data.nova_api;
1646
+
1647
+ // Check if any Nova API state has changed
1648
+ const novaStateChanged =
1649
+ prevNovaState.available !== novaApi.available ||
1650
+ prevNovaState.enabled !== novaApi.enabled ||
1651
+ prevNovaState.connected !== novaApi.connected ||
1652
+ prevNovaState.state_streaming !== novaApi.state_streaming ||
1653
+ prevNovaState.ik !== novaApi.ik;
1654
+
1655
+ if (novaStateChanged) {
1656
+ const stateController = document.getElementById('nova_state_controller');
1657
+ const ikController = document.getElementById('nova_ik_controller');
1658
+ const badge = document.getElementById('nova_connection_badge');
1659
+ const badgeTitle = document.getElementById('nova_badge_title');
1660
+ const modeText = document.getElementById('nova_mode_text');
1661
+
1662
+ // Only update toggle state if available or enabled changed
1663
+ if (prevNovaState.available !== novaApi.available || prevNovaState.enabled !== novaApi.enabled) {
1664
+ setNovaToggleState(novaApi.available, novaApi.enabled);
1665
+ }
1666
+
1667
+ // Track Nova state streaming status
1668
+ novaStateStreaming = novaApi.state_streaming || false;
1669
+
1670
+ // Detect Nova connection errors and show toast notifications
1671
+ const currentNovaConnected = novaApi.connected;
1672
+ if (prevNovaConnected !== null && prevNovaConnected !== currentNovaConnected) {
1673
+ if (!currentNovaConnected && novaApi.enabled) {
1674
+ // Connection was lost or failed to connect while Nova is enabled
1675
+ showClientNotification({
1676
+ message: 'Nova API connection lost. Check that Nova is running and accessible.',
1677
+ status: 'error'
1678
+ });
1679
+ } else if (currentNovaConnected && novaApi.enabled) {
1680
+ // Connection was restored
1681
+ showClientNotification({
1682
+ message: 'Nova API connection established.',
1683
+ status: 'info'
1684
+ });
1685
+ }
1686
+ }
1687
+ // Also check for enabled but not connected state (initial error)
1688
+ if (prevNovaConnected === null && novaApi.enabled && !currentNovaConnected) {
1689
  showClientNotification({
1690
+ message: 'Nova API is enabled but not connected. Waiting for Nova to be ready...',
1691
+ status: 'warning'
1692
  });
1693
  }
1694
+ prevNovaConnected = currentNovaConnected;
 
 
 
 
 
 
 
 
1695
 
1696
+ // Update badge visibility and classes
1697
+ if (badge) {
1698
+ if (prevNovaState.available !== novaApi.available) {
1699
+ badge.style.display = novaApi.available ? 'block' : 'none';
1700
+ }
1701
+ if (prevNovaState.enabled !== novaApi.enabled) {
1702
+ badge.classList.toggle('connected', novaApi.enabled);
1703
+ badge.classList.toggle('disconnected', !novaApi.enabled);
1704
+ }
1705
+ }
1706
 
1707
+ // Update controller indicators
1708
+ const connectedColor = novaApi.connected ? 'var(--wb-success)' : 'var(--wb-highlight)';
1709
+ if (stateController && (prevNovaState.state_streaming !== novaApi.state_streaming || prevNovaState.connected !== novaApi.connected)) {
1710
+ stateController.innerText = novaApi.state_streaming ? 'Nova API' : 'Internal';
1711
+ stateController.style.color = connectedColor;
1712
+ }
1713
+ if (ikController && (prevNovaState.ik !== novaApi.ik)) {
1714
+ ikController.innerText = novaApi.ik ? 'Nova API' : 'Internal';
1715
+ ikController.style.color = novaApi.ik ? 'var(--wb-success)' : 'var(--wb-highlight)';
1716
+ }
1717
 
1718
+ // Update badge title
1719
+ if (badgeTitle && (prevNovaState.enabled !== novaApi.enabled || prevNovaState.connected !== novaApi.connected)) {
1720
+ if (novaApi.enabled) {
1721
+ badgeTitle.innerText = novaApi.connected ? '🌐 Nova Connected' : '🌐 Nova Powering Up';
1722
+ } else {
1723
+ badgeTitle.innerText = '🌐 Nova Disabled';
1724
+ }
1725
  }
 
1726
 
1727
+ // Update mode text
1728
+ if (modeText) {
1729
+ let newModeText = '';
1730
+ if (novaApi.connected) {
1731
+ if (novaApi.state_streaming && novaApi.ik) {
1732
+ newModeText = 'Hybrid Mode';
1733
+ } else if (novaApi.state_streaming) {
1734
+ newModeText = 'Digital Twin Mode';
1735
+ } else if (novaApi.ik) {
1736
+ newModeText = 'Nova IK Mode';
1737
+ }
1738
+ } else if (novaApi.enabled) {
1739
+ newModeText = 'Awaiting connection';
1740
+ } else {
1741
+ newModeText = 'Internal control';
1742
+ }
1743
+ if (modeText.innerText !== newModeText) {
1744
+ modeText.innerText = newModeText;
1745
  }
 
 
 
 
1746
  }
1747
+
1748
+ // Store current state for next comparison
1749
+ prevNovaState = {
1750
+ available: novaApi.available,
1751
+ enabled: novaApi.enabled,
1752
+ connected: novaApi.connected,
1753
+ state_streaming: novaApi.state_streaming,
1754
+ ik: novaApi.ik
1755
+ };
1756
  }
1757
  }
1758