amsHelpers.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /**
  2. * AMS (Automatic Material System) helper utilities for Bambu Lab printers.
  3. * These functions handle color normalization, slot labeling, and tray ID calculations
  4. * for AMS, AMS-HT, and external spool configurations.
  5. */
  6. /**
  7. * Normalize color format from various sources.
  8. * API returns "RRGGBBAA" (8-char), 3MF uses "#RRGGBB" (7-char with hash).
  9. * This normalizes to "#RRGGBB" format.
  10. */
  11. export function normalizeColor(color: string | null | undefined): string {
  12. if (!color) return '#808080';
  13. // Remove alpha channel if present (8-char hex to 6-char)
  14. const hex = color.replace('#', '').substring(0, 6);
  15. return `#${hex}`;
  16. }
  17. /**
  18. * Normalize color for comparison (case-insensitive, strip hash and alpha).
  19. */
  20. export function normalizeColorForCompare(color: string | undefined): string {
  21. if (!color) return '';
  22. return color.replace('#', '').toLowerCase().substring(0, 6);
  23. }
  24. /**
  25. * Check if two colors are visually similar within a threshold.
  26. * Uses RGB component comparison with configurable tolerance.
  27. * @param color1 - First hex color
  28. * @param color2 - Second hex color
  29. * @param threshold - Maximum difference per RGB component (default: 40)
  30. */
  31. export function colorsAreSimilar(
  32. color1: string | undefined,
  33. color2: string | undefined,
  34. threshold = 40
  35. ): boolean {
  36. const hex1 = normalizeColorForCompare(color1);
  37. const hex2 = normalizeColorForCompare(color2);
  38. if (!hex1 || !hex2 || hex1.length < 6 || hex2.length < 6) return false;
  39. const r1 = parseInt(hex1.substring(0, 2), 16);
  40. const g1 = parseInt(hex1.substring(2, 4), 16);
  41. const b1 = parseInt(hex1.substring(4, 6), 16);
  42. const r2 = parseInt(hex2.substring(0, 2), 16);
  43. const g2 = parseInt(hex2.substring(2, 4), 16);
  44. const b2 = parseInt(hex2.substring(4, 6), 16);
  45. return (
  46. Math.abs(r1 - r2) <= threshold &&
  47. Math.abs(g1 - g2) <= threshold &&
  48. Math.abs(b1 - b2) <= threshold
  49. );
  50. }
  51. /**
  52. * Format slot label for display in the UI.
  53. * @param amsId - AMS unit ID (0-3 for regular AMS, 128+ for AMS-HT)
  54. * @param trayId - Tray/slot ID within the AMS unit (0-3)
  55. * @param isHt - Whether this is an AMS-HT unit (single tray)
  56. * @param isExternal - Whether this is the external spool holder
  57. */
  58. export function formatSlotLabel(
  59. amsId: number,
  60. trayId: number,
  61. isHt: boolean,
  62. isExternal: boolean
  63. ): string {
  64. if (isExternal) return 'External';
  65. // Convert AMS ID to letter (A, B, C, D)
  66. // AMS-HT uses IDs starting at 128
  67. const letter = String.fromCharCode(65 + (amsId >= 128 ? amsId - 128 : amsId));
  68. if (isHt) return `HT-${letter}`;
  69. return `AMS-${letter} Slot ${trayId + 1}`;
  70. }
  71. /**
  72. * Calculate global tray ID for MQTT command.
  73. * Used in the ams_mapping array sent to the printer.
  74. * @param amsId - AMS unit ID (0-3 for regular AMS, 128+ for AMS-HT)
  75. * @param trayId - Tray/slot ID within the AMS unit
  76. * @param isExternal - Whether this is the external spool holder
  77. * @returns Global tray ID (0-15 for AMS, 128+ for AMS-HT, 254 for external)
  78. */
  79. export function getGlobalTrayId(
  80. amsId: number,
  81. trayId: number,
  82. isExternal: boolean
  83. ): number {
  84. if (isExternal) return 254 + trayId;
  85. // AMS-HT units have IDs starting at 128 with a single tray — use ID directly
  86. if (amsId >= 128) return amsId;
  87. return amsId * 4 + trayId;
  88. }
  89. /**
  90. * Format seconds to human readable time string.
  91. */
  92. export function formatTime(seconds: number | null | undefined): string {
  93. if (!seconds) return '';
  94. const hours = Math.floor(seconds / 3600);
  95. const minutes = Math.floor((seconds % 3600) / 60);
  96. if (hours > 0) return `${hours}h ${minutes}m`;
  97. return `${minutes}m`;
  98. }
  99. /**
  100. * Get minimum datetime for scheduling (now + 1 minute).
  101. * Returns ISO string format for datetime-local input.
  102. */
  103. export function getMinDateTime(): string {
  104. const now = new Date();
  105. now.setMinutes(now.getMinutes() + 1);
  106. return now.toISOString().slice(0, 16);
  107. }
  108. /**
  109. * Check if a scheduled time is a placeholder far-future date.
  110. * Placeholder dates (more than 6 months out) are treated as ASAP.
  111. */
  112. export function isPlaceholderDate(scheduledTime: string | null | undefined): boolean {
  113. if (!scheduledTime) return false;
  114. const sixMonthsFromNow = Date.now() + 180 * 24 * 60 * 60 * 1000;
  115. return new Date(scheduledTime).getTime() > sixMonthsFromNow;
  116. }