SpoolBuddyAmsPage.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import { useEffect, useMemo } from 'react';
  2. import { useOutletContext } from 'react-router-dom';
  3. import { useQuery } from '@tanstack/react-query';
  4. import { useTranslation } from 'react-i18next';
  5. import { Layers } from 'lucide-react';
  6. import type { SpoolBuddyOutletContext } from '../../components/spoolbuddy/SpoolBuddyLayout';
  7. import { api } from '../../api/client';
  8. import type { PrinterStatus } from '../../api/client';
  9. import { AmsUnitCard } from '../../components/spoolbuddy/AmsUnitCard';
  10. function getAmsName(amsId: number): string {
  11. if (amsId <= 3) return `AMS ${String.fromCharCode(65 + amsId)}`;
  12. if (amsId >= 128 && amsId <= 135) return `AMS HT ${String.fromCharCode(65 + amsId - 128)}`;
  13. return `AMS ${amsId}`;
  14. }
  15. export function SpoolBuddyAmsPage() {
  16. const { selectedPrinterId, setAlert } = useOutletContext<SpoolBuddyOutletContext>();
  17. const { t } = useTranslation();
  18. const { data: status } = useQuery<PrinterStatus>({
  19. queryKey: ['printerStatus', selectedPrinterId],
  20. queryFn: () => api.getPrinterStatus(selectedPrinterId!),
  21. enabled: selectedPrinterId !== null,
  22. staleTime: 30 * 1000,
  23. });
  24. const isConnected = status?.connected ?? false;
  25. const amsUnits = useMemo(() => status?.ams ?? [], [status?.ams]);
  26. const trayNow = status?.tray_now ?? 255;
  27. const getActiveSlotForAms = (amsId: number): number | null => {
  28. if (trayNow === 255 || trayNow === 254) return null;
  29. if (amsId <= 3) {
  30. const activeAmsId = Math.floor(trayNow / 4);
  31. if (activeAmsId === amsId) return trayNow % 4;
  32. }
  33. // AMS-HT: tray_now 16-23 maps to AMS-HT 128-135
  34. if (amsId >= 128 && amsId <= 135) {
  35. const htIndex = amsId - 128;
  36. if (trayNow === 16 + htIndex) return 0;
  37. }
  38. return null;
  39. };
  40. // Set alert for low filament in status bar
  41. useEffect(() => {
  42. if (!isConnected && selectedPrinterId) {
  43. setAlert({ type: 'warning', message: t('spoolbuddy.ams.printerDisconnected', 'Printer disconnected') });
  44. return;
  45. }
  46. for (const unit of amsUnits) {
  47. for (const tray of unit.tray || []) {
  48. if (tray.remain !== null && tray.remain >= 0 && tray.remain < 15 && tray.tray_type) {
  49. setAlert({
  50. type: 'warning',
  51. message: `Low Filament: ${tray.tray_type} (${getAmsName(unit.id)}) - ${tray.remain}% remaining`,
  52. });
  53. return;
  54. }
  55. }
  56. }
  57. setAlert(null);
  58. }, [amsUnits, isConnected, selectedPrinterId, setAlert, t]);
  59. return (
  60. <div className="h-full flex flex-col p-4">
  61. <div className="flex-1 min-h-0 overflow-y-auto">
  62. {!selectedPrinterId ? (
  63. <div className="flex-1 flex items-center justify-center h-full">
  64. <div className="text-center text-white/50">
  65. <p className="text-lg mb-2">{t('spoolbuddy.ams.noPrinter', 'No printer selected')}</p>
  66. <p className="text-sm">{t('spoolbuddy.ams.selectPrinter', 'Select a printer from the top bar')}</p>
  67. </div>
  68. </div>
  69. ) : !isConnected ? (
  70. <div className="flex-1 flex items-center justify-center h-full">
  71. <div className="text-center text-white/50">
  72. <p className="text-lg mb-2">{t('spoolbuddy.ams.printerDisconnected', 'Printer disconnected')}</p>
  73. </div>
  74. </div>
  75. ) : amsUnits.length === 0 ? (
  76. <div className="flex-1 flex items-center justify-center h-full">
  77. <div className="text-center text-white/50">
  78. <Layers className="w-12 h-12 mx-auto mb-3 opacity-50" />
  79. <p className="text-lg mb-2">{t('spoolbuddy.ams.noData', 'No AMS detected')}</p>
  80. <p className="text-sm">{t('spoolbuddy.ams.connectAms', 'Connect an AMS to see filament slots')}</p>
  81. </div>
  82. </div>
  83. ) : (
  84. <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
  85. {amsUnits.map((unit) => (
  86. <AmsUnitCard
  87. key={unit.id}
  88. unit={unit}
  89. activeSlot={getActiveSlotForAms(unit.id)}
  90. />
  91. ))}
  92. </div>
  93. )}
  94. </div>
  95. </div>
  96. );
  97. }