trayIds.test.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /**
  2. * Parity with the backend: the cases of backend/tests/unit/test_tray_ids.py
  3. * against utils/trayIds (and the amsHelpers wrappers built on it), with the
  4. * backend's expected values. Backend-only parts (parsing `info`, raw tray_now,
  5. * wire encodings, loaded-tray test, tray bits) have no frontend twin.
  6. */
  7. import { describe, expect, it } from 'vitest';
  8. import {
  9. AmsType,
  10. externalHotend,
  11. externalSlotIndex,
  12. externalTrayId,
  13. globalTrayId,
  14. isExternalTray,
  15. isRackAms,
  16. layoutAmsType,
  17. locateTray,
  18. trayLayoutOf,
  19. unitAmsType,
  20. type TrayLayout,
  21. } from '../../utils/trayIds';
  22. import { getAmsLabel, getGlobalTrayId } from '../../utils/amsHelpers';
  23. // The H3C as the status API reports it.
  24. const H3C_UNITS = [{ id: 32, ams_type: AmsType.ZMULTI_A }];
  25. const H3C_VT = Array.from({ length: 9 }, (_, k) => ({ id: 247 + k }));
  26. const H3C = trayLayoutOf(H3C_UNITS, H3C_VT);
  27. // The backend's TrayLayout.slot_key / tray_from_slot_key, from the frontend pieces.
  28. function slotKey(layout: TrayLayout, trayId: number): [number, number] | null {
  29. if (isExternalTray(trayId, true)) return [255, externalSlotIndex(trayId)];
  30. return locateTray(layout, trayId);
  31. }
  32. function trayFromSlotKey(layout: TrayLayout, amsId: number, trayId: number): number {
  33. return getGlobalTrayId(amsId, trayId, amsId === 255, layoutAmsType(layout, amsId));
  34. }
  35. describe('AMS type', () => {
  36. it('is told from the id without a type', () => {
  37. expect(unitAmsType(0)).toBe(AmsType.AMS);
  38. expect(unitAmsType(3)).toBe(AmsType.AMS);
  39. expect(unitAmsType(128)).toBe(AmsType.N3S);
  40. expect(unitAmsType(135)).toBe(AmsType.N3S);
  41. expect(unitAmsType(32)).toBe(AmsType.ZMULTI_A);
  42. });
  43. it('takes the reported type over the id, except for an AMS-HT', () => {
  44. expect(unitAmsType(32, AmsType.ZMULTI_B)).toBe(AmsType.ZMULTI_B);
  45. expect(unitAmsType(128, AmsType.AMS)).toBe(AmsType.N3S);
  46. expect(unitAmsType(5, null)).toBe(AmsType.AMS);
  47. expect(unitAmsType(5, AmsType.EXT_SPOOL)).toBe(AmsType.AMS);
  48. });
  49. it('knows the rack types', () => {
  50. expect(isRackAms(AmsType.ZMULTI_A) && isRackAms(AmsType.ZMULTI_B)).toBe(true);
  51. for (const kind of [AmsType.AMS, AmsType.N3F, AmsType.N3S, AmsType.AMS_LITE_MIXED]) {
  52. expect(isRackAms(kind)).toBe(false);
  53. }
  54. });
  55. });
  56. describe('global tray id', () => {
  57. it.each([
  58. [0, 0, AmsType.AMS, 0],
  59. [1, 2, AmsType.AMS, 6],
  60. [3, 3, AmsType.N3F, 15],
  61. [6, 1, AmsType.AMS_LITE, 25], // the A2L's AMS Lite, stored as unit 6
  62. [16, 1, AmsType.AMS_LITE_MIXED, 25], // ...and Studio's own numbering of its unit 16
  63. [32, 0, AmsType.ZMULTI_A, 28],
  64. [32, 8, AmsType.ZMULTI_A, 36],
  65. [33, 0, AmsType.ZMULTI_B, 37],
  66. [33, 4, AmsType.ZMULTI_B, 41],
  67. [128, 0, AmsType.N3S, 128],
  68. [135, 0, AmsType.N3S, 135],
  69. ])('unit %i slot %i (type %i) is %i', (amsId, slot, kind, expected) => {
  70. expect(globalTrayId(amsId, slot, kind)).toBe(expected);
  71. expect(getGlobalTrayId(amsId, slot, false, kind)).toBe(expected);
  72. });
  73. it('tells a regular AMS, an AMS-HT and the AMS 3H by their ids without a type', () => {
  74. expect([0, 1, 2, 3].map((slot) => globalTrayId(1, slot))).toEqual([4, 5, 6, 7]);
  75. expect(globalTrayId(129, 0)).toBe(129);
  76. expect(Array.from({ length: 9 }, (_, slot) => globalTrayId(32, slot))).toEqual([28, 29, 30, 31, 32, 33, 34, 35, 36]);
  77. });
  78. it('keeps the AMS 3H out of the AMS-HT and regular ranges', () => {
  79. for (let slot = 0; slot < 9; slot++) {
  80. const tray = globalTrayId(32, slot);
  81. expect(tray < 128 || tray > 135).toBe(true);
  82. expect(tray).toBeGreaterThanOrEqual(16);
  83. }
  84. });
  85. });
  86. describe('layout', () => {
  87. it('reads the H3C as a rack printer', () => {
  88. expect(H3C.rack).toBe(true);
  89. expect(H3C.units).toEqual([{ id: 32, type: AmsType.ZMULTI_A }]);
  90. expect(layoutAmsType(H3C, 32)).toBe(AmsType.ZMULTI_A);
  91. });
  92. it('numbers the H3C both ways', () => {
  93. for (let slot = 0; slot < 9; slot++) {
  94. const tray = globalTrayId(32, slot, layoutAmsType(H3C, 32));
  95. expect(tray).toBe(28 + slot);
  96. expect(locateTray(H3C, tray)).toEqual([32, slot]);
  97. expect(slotKey(H3C, tray)).toEqual([32, slot]);
  98. expect(trayFromSlotKey(H3C, 32, slot)).toBe(tray);
  99. }
  100. });
  101. it('numbers a regular printer both ways, unchanged', () => {
  102. const layout = trayLayoutOf([{ id: 0, ams_type: AmsType.AMS }, { id: 1, ams_type: AmsType.AMS }, { id: 128 }]);
  103. expect(layout.rack).toBe(false);
  104. for (const amsId of [0, 1]) {
  105. for (let slot = 0; slot < 4; slot++) {
  106. const tray = globalTrayId(amsId, slot, layoutAmsType(layout, amsId));
  107. expect(tray).toBe(amsId * 4 + slot);
  108. expect(locateTray(layout, tray)).toEqual([amsId, slot]);
  109. expect(trayFromSlotKey(layout, amsId, slot)).toBe(tray);
  110. expect(slotKey(layout, tray)).toEqual([amsId, slot]);
  111. }
  112. }
  113. expect(globalTrayId(128, 0, layoutAmsType(layout, 128))).toBe(128);
  114. expect(locateTray(layout, 128)).toEqual([128, 0]);
  115. });
  116. it('agrees both ways without live data', () => {
  117. // No AMS data: 28-36 are the AMS 3H's (unit 32), 247-255 external inputs.
  118. const empty = trayLayoutOf([]);
  119. for (let slot = 0; slot < 9; slot++) {
  120. expect(trayFromSlotKey(empty, 32, slot)).toBe(28 + slot);
  121. expect(slotKey(empty, 28 + slot)).toEqual([32, slot]);
  122. }
  123. for (let index = 0; index < 9; index++) {
  124. expect(slotKey(empty, trayFromSlotKey(empty, 255, index))).toEqual([255, index]);
  125. }
  126. });
  127. it('keeps 28-31 for a listed regular unit 7', () => {
  128. const layout = trayLayoutOf([{ id: 7, ams_type: AmsType.AMS }]);
  129. expect(slotKey(layout, 28)).toEqual([7, 0]);
  130. expect(slotKey(layout, 31)).toEqual([7, 3]);
  131. expect(trayFromSlotKey(layout, 7, 2)).toBe(30);
  132. });
  133. it('numbers an AMS-B by its type', () => {
  134. const layout = trayLayoutOf([{ id: 40, ams_type: AmsType.ZMULTI_B }]);
  135. expect(layout.rack).toBe(true);
  136. expect(globalTrayId(40, 2, layoutAmsType(layout, 40))).toBe(39);
  137. expect(locateTray(layout, 39)).toEqual([40, 2]);
  138. });
  139. it('takes an explicit type from the API', () => {
  140. const layout = trayLayoutOf([{ id: 32, ams_type: 6 }]);
  141. expect(globalTrayId(32, 4, layoutAmsType(layout, 32))).toBe(32);
  142. expect(locateTray(layout, 32)).toEqual([32, 4]);
  143. });
  144. it('counts a unit listed twice once', () => {
  145. const layout = trayLayoutOf([{ id: 32, ams_type: 6 }, { id: 32, ams_type: 6 }]);
  146. expect(layout.units).toEqual([{ id: 32, type: AmsType.ZMULTI_A }]);
  147. expect(locateTray(layout, 32)).toEqual([32, 4]);
  148. });
  149. });
  150. describe('external inputs', () => {
  151. it('are 254 and 255 everywhere', () => {
  152. for (const tray of [254, 255]) {
  153. expect(isExternalTray(tray)).toBe(true);
  154. expect(isExternalTray(tray, true)).toBe(true);
  155. }
  156. });
  157. it('are 247-253 only on a rack printer', () => {
  158. for (let tray = 247; tray <= 253; tray++) {
  159. expect(isExternalTray(tray)).toBe(false);
  160. expect(isExternalTray(tray, true)).toBe(true);
  161. expect(isExternalTray(tray, H3C.rack)).toBe(true);
  162. }
  163. expect(isExternalTray(250, trayLayoutOf([{ id: 0 }], [{ id: 254 }, { id: 255 }]).rack)).toBe(false);
  164. });
  165. it('never include an AMS slot', () => {
  166. for (const tray of [0, 15, 28, 36, 128]) expect(isExternalTray(tray, true)).toBe(false);
  167. });
  168. it('feed hotend 255 - id', () => {
  169. expect([255, 254, 253, 252, 251, 250, 249, 248, 247].map(externalHotend)).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8]);
  170. });
  171. it('are stored under ams_id 255 as tray 0-8', () => {
  172. expect(externalSlotIndex(254)).toBe(0);
  173. expect(externalSlotIndex(255)).toBe(1);
  174. expect([247, 248, 249, 250, 251, 252, 253].map(externalSlotIndex)).toEqual([2, 3, 4, 5, 6, 7, 8]);
  175. for (let tray = 247; tray <= 255; tray++) {
  176. expect(externalTrayId(externalSlotIndex(tray))).toBe(tray);
  177. expect(slotKey(H3C, tray)).toEqual([255, externalSlotIndex(tray)]);
  178. expect(trayFromSlotKey(H3C, 255, externalSlotIndex(tray))).toBe(tray);
  179. }
  180. expect(locateTray(H3C, 250)).toBeNull();
  181. });
  182. it('make a rack printer when one of 247-253 is reported', () => {
  183. expect(trayLayoutOf([], H3C_VT).rack).toBe(true);
  184. expect(trayLayoutOf([], [{ id: 254 }, { id: 255 }]).rack).toBe(false);
  185. });
  186. it('keep their slot keys on a regular printer', () => {
  187. const layout = trayLayoutOf([{ id: 0 }], [{ id: 254 }]);
  188. expect(slotKey(layout, 254)).toEqual([255, 0]);
  189. expect(slotKey(layout, 255)).toEqual([255, 1]);
  190. // 247-253 are only ever external inputs, so they keep their stored key everywhere.
  191. expect(slotKey(layout, 250)).toEqual([255, 5]);
  192. expect(locateTray(layout, 250)).toBeNull();
  193. });
  194. });
  195. describe('unit labels', () => {
  196. it('match the backend', () => {
  197. expect(getAmsLabel(0)).toBe('AMS-A');
  198. expect(getAmsLabel(129)).toBe('HT-B');
  199. expect(getAmsLabel(32)).toBe('AMS 3H');
  200. });
  201. });
  202. describe('model fallback', () => {
  203. it('gives an H3C without AMS data its AMS 3H', () => {
  204. const ams3h = { units: [{ id: 32, type: AmsType.ZMULTI_A }], rack: true };
  205. expect(trayLayoutOf(null, null, 'H3C')).toEqual(ams3h);
  206. expect(trayLayoutOf([], [], 'N10')).toEqual(ams3h);
  207. expect(slotKey(trayLayoutOf(null, null, 'H3C'), 250)).toEqual([255, 5]);
  208. expect(isExternalTray(250, trayLayoutOf(null, null, 'H3C').rack)).toBe(true);
  209. expect(trayLayoutOf(null, null, 'X1C').rack).toBe(false);
  210. expect(trayLayoutOf(null, null).rack).toBe(false);
  211. });
  212. it('never outranks live units', () => {
  213. expect(trayLayoutOf([{ id: 0, ams_type: 1 }], [{ id: 254 }], 'H3C').rack).toBe(false);
  214. });
  215. });