@fmsim/machine 2.0.0-beta.7 → 2.0.0-beta.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/dist/agv.js +2 -2
  2. package/dist/agv.js.map +1 -1
  3. package/dist/carrier-manager.js +93 -0
  4. package/dist/carrier-manager.js.map +1 -0
  5. package/dist/carrier.js +81 -5
  6. package/dist/carrier.js.map +1 -1
  7. package/dist/conveyor-join.js +8 -2
  8. package/dist/conveyor-join.js.map +1 -1
  9. package/dist/conveyor.js +8 -0
  10. package/dist/conveyor.js.map +1 -1
  11. package/dist/crane.js.map +1 -1
  12. package/dist/factories/agv-3d.js +8 -9
  13. package/dist/factories/agv-3d.js.map +1 -1
  14. package/dist/factories/conveyor-3d.js +166 -81
  15. package/dist/factories/conveyor-3d.js.map +1 -1
  16. package/dist/factories/crane-3d.js +5 -7
  17. package/dist/factories/crane-3d.js.map +1 -1
  18. package/dist/factories/crane-rail-3d.js +4 -6
  19. package/dist/factories/crane-rail-3d.js.map +1 -1
  20. package/dist/factories/machine-3d.js +384 -133
  21. package/dist/factories/machine-3d.js.map +1 -1
  22. package/dist/factories/oht-3d.js +20 -20
  23. package/dist/factories/oht-3d.js.map +1 -1
  24. package/dist/factories/shuttle-3d.js +2 -5
  25. package/dist/factories/shuttle-3d.js.map +1 -1
  26. package/dist/features/conveyor-mixin.js.map +1 -1
  27. package/dist/features/mcs-status-mixin.js +22 -0
  28. package/dist/features/mcs-status-mixin.js.map +1 -1
  29. package/dist/features/mcs-zone-mixin.js.map +1 -1
  30. package/dist/features/parent-object-mixin.js.map +1 -1
  31. package/dist/floating-container.js +5 -0
  32. package/dist/floating-container.js.map +1 -1
  33. package/dist/index.js +50 -34
  34. package/dist/index.js.map +1 -1
  35. package/dist/mcs-carrier-holder.js +39 -22
  36. package/dist/mcs-carrier-holder.js.map +1 -1
  37. package/dist/mcs-machine.js +28 -0
  38. package/dist/mcs-machine.js.map +1 -1
  39. package/dist/mcs-unit.js +10 -0
  40. package/dist/mcs-unit.js.map +1 -1
  41. package/dist/mcs-vehicle.js +8 -3
  42. package/dist/mcs-vehicle.js.map +1 -1
  43. package/dist/node.js +1 -1
  44. package/dist/node.js.map +1 -1
  45. package/dist/oht.js.map +1 -1
  46. package/dist/scanner.js +30 -0
  47. package/dist/scanner.js.map +1 -0
  48. package/dist/scene/root-container-override.js +9 -0
  49. package/dist/scene/root-container-override.js.map +1 -1
  50. package/dist/static-container.js +5 -0
  51. package/dist/static-container.js.map +1 -1
  52. package/dist/stocker-abnormal-bar.js +1 -1
  53. package/dist/stocker-abnormal-bar.js.map +1 -1
  54. package/dist/stocker-capacity-bar.js +3 -3
  55. package/dist/stocker-capacity-bar.js.map +1 -1
  56. package/dist/stocker-carrier-bar.js +1 -1
  57. package/dist/stocker-carrier-bar.js.map +1 -1
  58. package/dist/stocker.js +6 -0
  59. package/dist/stocker.js.map +1 -1
  60. package/dist/templates/backdrop.js +30 -0
  61. package/dist/templates/backdrop.js.map +1 -0
  62. package/dist/templates/decal.js +21 -0
  63. package/dist/templates/decal.js.map +1 -0
  64. package/dist/templates/index.js +7 -1
  65. package/dist/templates/index.js.map +1 -1
  66. package/dist/templates/scanner.js +20 -0
  67. package/dist/templates/scanner.js.map +1 -0
  68. package/dist/tsconfig.tsbuildinfo +1 -1
  69. package/dist/utils/get-value-on-ranges.js +2 -4
  70. package/dist/utils/get-value-on-ranges.js.map +1 -1
  71. package/dist/zone-capacity-bar.js +3 -3
  72. package/dist/zone-capacity-bar.js.map +1 -1
  73. package/icons/backdrop.png +0 -0
  74. package/icons/decal.png +0 -0
  75. package/jest.config.js +10 -0
  76. package/package.json +4 -3
  77. package/src/agv.ts +2 -2
  78. package/src/carrier-manager.ts +117 -0
  79. package/src/carrier.ts +80 -3
  80. package/src/conveyor-join.ts +5 -2
  81. package/src/conveyor.ts +8 -0
  82. package/src/crane.ts +1 -1
  83. package/src/factories/agv-3d.ts +9 -10
  84. package/src/factories/conveyor-3d.ts +168 -84
  85. package/src/factories/crane-3d.ts +6 -8
  86. package/src/factories/crane-rail-3d.ts +5 -7
  87. package/src/factories/machine-3d.ts +452 -145
  88. package/src/factories/oht-3d.ts +20 -20
  89. package/src/factories/shuttle-3d.ts +2 -6
  90. package/src/features/conveyor-mixin.ts +1 -1
  91. package/src/features/mcs-status-mixin.ts +26 -4
  92. package/src/features/mcs-zone-mixin.ts +1 -1
  93. package/src/features/parent-object-mixin.ts +1 -1
  94. package/src/floating-container.ts +5 -0
  95. package/src/index.ts +56 -34
  96. package/src/mcs-carrier-holder.ts +38 -34
  97. package/src/mcs-machine.ts +29 -0
  98. package/src/mcs-unit.ts +12 -1
  99. package/src/mcs-vehicle.ts +9 -6
  100. package/src/node.ts +1 -1
  101. package/src/oht.ts +1 -1
  102. package/src/scanner.ts +29 -0
  103. package/src/scene/root-container-override.ts +12 -0
  104. package/src/static-container.ts +5 -0
  105. package/src/stocker-abnormal-bar.ts +1 -1
  106. package/src/stocker-capacity-bar.ts +3 -3
  107. package/src/stocker-carrier-bar.ts +1 -1
  108. package/src/stocker.ts +7 -0
  109. package/src/templates/backdrop.ts +30 -0
  110. package/src/templates/decal.ts +21 -0
  111. package/src/templates/index.ts +7 -1
  112. package/src/templates/scanner.ts +20 -0
  113. package/src/utils/get-value-on-ranges.ts +2 -4
  114. package/src/zone-capacity-bar.ts +3 -3
  115. package/translations/en.json +16 -1
  116. package/translations/ja.json +16 -1
  117. package/translations/ko.json +16 -1
  118. package/translations/ms.json +16 -1
  119. package/translations/zh.json +16 -1
@@ -1,4 +1,4 @@
1
- export function getVaueOnRanges(value, param) {
1
+ export function getValueOnRanges(value, param) {
2
2
  for (let range in param) {
3
3
  let [from, to] = range.split('~');
4
4
  let from_defined = !!from;
@@ -6,14 +6,12 @@ export function getVaueOnRanges(value, param) {
6
6
  from = parseFloat(from);
7
7
  to = parseFloat(to);
8
8
  value = Number(value) || 0;
9
- // from이나 to 둘 중 하나만 있는 경우 먼저 계산
10
9
  if (from_defined && !to_defined && value >= from) {
11
10
  return param[range];
12
11
  }
13
- else if (!from_defined && to_defined && value < from) {
12
+ else if (!from_defined && to_defined && value < to) {
14
13
  return param[range];
15
14
  }
16
- // 소수점이 있으므로 from <= value < to 가 되야함
17
15
  if (value >= from && value < to) {
18
16
  return param[range];
19
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"get-value-on-ranges.js","sourceRoot":"","sources":["../../src/utils/get-value-on-ranges.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,eAAe,CAAC,KAAK,EAAE,KAAK;IAC1C,KAAK,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,GAAwB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACtD,IAAI,YAAY,GAAG,CAAC,CAAC,IAAI,CAAA;QACzB,IAAI,UAAU,GAAG,CAAC,CAAC,EAAE,CAAA;QAErB,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;QACvB,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC,CAAA;QACnB,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAE1B,gCAAgC;QAChC,IAAI,YAAY,IAAI,CAAC,UAAU,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YACjD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;aAAM,IAAI,CAAC,YAAY,IAAI,UAAU,IAAI,KAAK,GAAG,IAAI,EAAE,CAAC;YACvD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;QAED,sCAAsC;QACtC,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,SAAS,CAAC,CAAA;AACzB,CAAC","sourcesContent":["export function getVaueOnRanges(value, param): any {\n for (let range in param) {\n let [from, to]: (number | string)[] = range.split('~')\n let from_defined = !!from\n let to_defined = !!to\n\n from = parseFloat(from)\n to = parseFloat(to)\n value = Number(value) || 0\n\n // from이나 to 둘 중 하나만 있는 경우 먼저 계산\n if (from_defined && !to_defined && value >= from) {\n return param[range]\n } else if (!from_defined && to_defined && value < from) {\n return param[range]\n }\n\n // 소수점이 있으므로 from <= value < to 가 되야함\n if (value >= from && value < to) {\n return param[range]\n }\n }\n\n return param['default']\n}\n"]}
1
+ {"version":3,"file":"get-value-on-ranges.js","sourceRoot":"","sources":["../../src/utils/get-value-on-ranges.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAAC,KAAK,EAAE,KAAK;IAC3C,KAAK,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,GAAwB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACtD,IAAI,YAAY,GAAG,CAAC,CAAC,IAAI,CAAA;QACzB,IAAI,UAAU,GAAG,CAAC,CAAC,EAAE,CAAA;QAErB,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;QACvB,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC,CAAA;QACnB,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAE1B,IAAI,YAAY,IAAI,CAAC,UAAU,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YACjD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;aAAM,IAAI,CAAC,YAAY,IAAI,UAAU,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;YACrD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;QAED,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,SAAS,CAAC,CAAA;AACzB,CAAC","sourcesContent":["export function getValueOnRanges(value, param): any {\n for (let range in param) {\n let [from, to]: (number | string)[] = range.split('~')\n let from_defined = !!from\n let to_defined = !!to\n\n from = parseFloat(from)\n to = parseFloat(to)\n value = Number(value) || 0\n\n if (from_defined && !to_defined && value >= from) {\n return param[range]\n } else if (!from_defined && to_defined && value < to) {\n return param[range]\n }\n\n if (value >= from && value < to) {\n return param[range]\n }\n }\n\n return param['default']\n}\n"]}
@@ -3,7 +3,7 @@ import { sceneComponent } from '@hatiolab/things-scene';
3
3
  import { themesColorRange } from '@fmsim/api';
4
4
  import { LEGEND_CAPACITY } from './features/mcs-status-default';
5
5
  import { safeRound } from './utils/safe-round';
6
- import { getVaueOnRanges } from './utils/get-value-on-ranges';
6
+ import { getValueOnRanges } from './utils/get-value-on-ranges';
7
7
  import { MCSZoneMixin, MCSZoneMixinProperties } from './features/mcs-zone-mixin';
8
8
  import { ParentObjectMixinProperties } from './features/parent-object-mixin';
9
9
  import MCSUnit from './mcs-unit';
@@ -94,7 +94,7 @@ let ZoneCapacityBar = class ZoneCapacityBar extends MCSZoneMixin(MCSUnit) {
94
94
  }
95
95
  get statusColor() {
96
96
  const { fullup } = this.state;
97
- return fullup ? 'red' : getVaueOnRanges(this.usage, this.legend);
97
+ return fullup ? 'red' : getValueOnRanges(this.usage, this.legend);
98
98
  }
99
99
  get text() {
100
100
  return this.usage + '%';
@@ -215,7 +215,7 @@ let ZoneCapacityBar = class ZoneCapacityBar extends MCSZoneMixin(MCSUnit) {
215
215
  // intentionally have done nothing
216
216
  }
217
217
  onchangeData(after, before) {
218
- const { CURRENTCAPACITY = 0, MAXCAPACITY = 100, HIGHWATERMARK = 100, LOWWATERMARK = 0, FULLUP = 'F' } = typeof this.data == 'object' ? this.data : {};
218
+ const { CURRENTCAPACITY = 0, MAXCAPACITY = 100, HIGHWATERMARK = 100, LOWWATERMARK = 0, FULLUP = 'F' } = this.data && typeof this.data == 'object' ? this.data : {};
219
219
  const currentUsage = Number(CURRENTCAPACITY) || 0;
220
220
  const maxCapacity = Number(MAXCAPACITY) || 100;
221
221
  const highWatermark = Number(HIGHWATERMARK) || 100;
@@ -1 +1 @@
1
- {"version":3,"file":"zone-capacity-bar.js","sourceRoot":"","sources":["../src/zone-capacity-bar.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ComponentNature, POSITION, Properties, sceneComponent } from '@hatiolab/things-scene'\nimport { themesColorRange } from '@fmsim/api'\nimport { LEGEND_CAPACITY } from './features/mcs-status-default'\nimport { safeRound } from './utils/safe-round'\nimport { getVaueOnRanges } from './utils/get-value-on-ranges'\nimport { MCSZoneMixin, MCSZoneMixinProperties } from './features/mcs-zone-mixin'\nimport { ParentObjectMixinProperties } from './features/parent-object-mixin'\nimport MCSUnit from './mcs-unit'\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n ...MCSZoneMixinProperties,\n ...ParentObjectMixinProperties,\n {\n type: 'select',\n label: 'legend-name',\n name: 'legendName',\n property: {\n options: themesColorRange\n }\n },\n {\n type: 'number',\n name: 'currentUsage',\n label: 'current-usage'\n },\n {\n type: 'number',\n name: 'highWatermark',\n label: 'high-watermark'\n },\n {\n type: 'number',\n name: 'lowWatermark',\n label: 'low-watermark'\n },\n {\n type: 'number',\n name: 'maxCapacity',\n label: 'max-capacity'\n },\n {\n type: 'boolean',\n name: 'fullup',\n label: 'fullup'\n },\n {\n type: 'number',\n label: 'round',\n name: 'round',\n property: {\n min: 0\n }\n },\n {\n type: 'hidden',\n name: 'usage',\n label: 'usage'\n }\n ],\n 'value-property': 'usage'\n}\n\nvar controlHandler = {\n ondragmove: function (point: POSITION, index: number, component: Component) {\n var { left, top, width, height } = component.model\n\n var transcoorded = component.transcoordP2S(point.x, point.y)\n var round = ((transcoorded.x - left) / (width / 2)) * 100\n\n round = safeRound(round, width, height)\n\n component.set({\n round\n })\n }\n}\n\n@sceneComponent('ZoneCapacityBar')\nexport default class ZoneCapacityBar extends MCSZoneMixin(MCSUnit) {\n static get nature() {\n return NATURE\n }\n\n get controls() {\n var { left, top, width, round, height } = this.state\n round = round == undefined ? 0 : safeRound(round, width, height)\n\n return [\n {\n x: left + (width / 2) * (round / 100),\n y: top,\n handler: controlHandler\n }\n ]\n }\n\n getLegendFallback() {\n return (this.root as any).zoneCapacityLegendTheme || LEGEND_CAPACITY\n }\n\n get statusColor() {\n const { fullup } = this.state\n return fullup ? 'red' : getVaueOnRanges(this.usage, this.legend)\n }\n\n get text() {\n return this.usage + '%'\n }\n\n render(context: CanvasRenderingContext2D) {\n const { left, top, width, height } = this.bounds\n\n if (width >= height) {\n this.renderLandscape(context)\n } else {\n this.renderPortrait(context)\n }\n }\n\n renderLandscape(context: CanvasRenderingContext2D) {\n const { left, top, width, height } = this.bounds\n const { highWatermark, lowWatermark, lineWidth, strokeStyle, round, fillStyle = 'white' } = this.state\n\n context.save()\n context.translate(left, top)\n\n // gauge background\n context.beginPath()\n\n context.fillStyle = fillStyle\n context.roundRect(0, 0, width, height, round)\n\n context.fill()\n context.stroke()\n\n context.clip()\n\n // gauge foreground\n context.beginPath()\n\n context.fillStyle = this.statusColor!\n\n context.rect(0, 0, (width / 100) * this.usage, height)\n context.fill()\n\n // high-watermark level\n\n if (highWatermark && highWatermark > 0 && highWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const x = (width / 100) * highWatermark\n context.moveTo(x, 0)\n context.lineTo(x, height)\n\n context.stroke()\n }\n\n // low-watermark level\n\n if (lowWatermark && lowWatermark > 0 && lowWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const x = (width / 100) * lowWatermark\n context.moveTo(x, 0)\n context.lineTo(x, height)\n\n context.stroke()\n }\n\n // gauge outline\n context.beginPath()\n\n context.setLineDash([])\n context.strokeStyle = strokeStyle\n context.lineWidth = lineWidth\n context.roundRect(0, 0, width, height, round)\n context.stroke()\n\n context.beginPath()\n\n context.translate(-left, -top)\n context.restore()\n }\n\n renderPortrait(context: CanvasRenderingContext2D) {\n const { left, top, width, height } = this.bounds\n const { highWatermark, lowWatermark, lineWidth, strokeStyle, round, fillStyle = 'white' } = this.state\n\n context.save()\n context.translate(left, top)\n\n // gauge background\n context.beginPath()\n\n context.fillStyle = fillStyle\n context.roundRect(0, 0, width, height, round)\n\n context.fill()\n\n context.clip()\n\n // gauge foreground\n context.beginPath()\n\n context.fillStyle = this.statusColor!\n\n context.rect(0, height - (height / 100) * this.usage, width, (height / 100) * this.usage)\n context.fill()\n\n // high-watermark level\n\n if (highWatermark && highWatermark > 0 && highWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const y = height - (height / 100) * highWatermark\n context.moveTo(0, y)\n context.lineTo(width, y)\n\n context.stroke()\n }\n\n // low-watermark level\n\n if (lowWatermark && lowWatermark > 0 && lowWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const y = height - (height / 100) * lowWatermark\n context.moveTo(0, y)\n context.lineTo(width, y)\n\n context.stroke()\n }\n\n // gauge outline\n context.beginPath()\n\n context.setLineDash([])\n context.strokeStyle = strokeStyle\n context.lineWidth = lineWidth\n context.roundRect(0, 0, width, height, round)\n context.stroke()\n\n context.beginPath()\n\n context.translate(-left, -top)\n context.restore()\n }\n\n get usage() {\n const { currentUsage = 0, maxCapacity = 100 } = this.state\n return Math.round((currentUsage / maxCapacity) * 100)\n }\n\n set usage(usage: number) {\n // intentionally have done nothing\n }\n\n onchangeData(after: Record<string, any>, before: Record<string, any>): void {\n const {\n CURRENTCAPACITY = 0,\n MAXCAPACITY = 100,\n HIGHWATERMARK = 100,\n LOWWATERMARK = 0,\n FULLUP = 'F'\n } = typeof this.data == 'object' ? this.data : {}\n\n const currentUsage = Number(CURRENTCAPACITY) || 0\n const maxCapacity = Number(MAXCAPACITY) || 100\n const highWatermark = Number(HIGHWATERMARK) || 100\n const lowWatermark = Number(LOWWATERMARK) || 0\n\n this.setState({\n currentUsage,\n maxCapacity,\n highWatermark,\n lowWatermark,\n fullup: FULLUP == 'T',\n usage: Math.round((currentUsage / maxCapacity) * 100)\n })\n }\n}\n"]}
1
+ {"version":3,"file":"zone-capacity-bar.js","sourceRoot":"","sources":["../src/zone-capacity-bar.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ComponentNature, POSITION, Properties, sceneComponent } from '@hatiolab/things-scene'\nimport { themesColorRange } from '@fmsim/api'\nimport { LEGEND_CAPACITY } from './features/mcs-status-default'\nimport { safeRound } from './utils/safe-round'\nimport { getValueOnRanges } from './utils/get-value-on-ranges'\nimport { MCSZoneMixin, MCSZoneMixinProperties } from './features/mcs-zone-mixin'\nimport { ParentObjectMixinProperties } from './features/parent-object-mixin'\nimport MCSUnit from './mcs-unit'\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n ...MCSZoneMixinProperties,\n ...ParentObjectMixinProperties,\n {\n type: 'select',\n label: 'legend-name',\n name: 'legendName',\n property: {\n options: themesColorRange\n }\n },\n {\n type: 'number',\n name: 'currentUsage',\n label: 'current-usage'\n },\n {\n type: 'number',\n name: 'highWatermark',\n label: 'high-watermark'\n },\n {\n type: 'number',\n name: 'lowWatermark',\n label: 'low-watermark'\n },\n {\n type: 'number',\n name: 'maxCapacity',\n label: 'max-capacity'\n },\n {\n type: 'boolean',\n name: 'fullup',\n label: 'fullup'\n },\n {\n type: 'number',\n label: 'round',\n name: 'round',\n property: {\n min: 0\n }\n },\n {\n type: 'hidden',\n name: 'usage',\n label: 'usage'\n }\n ],\n 'value-property': 'usage'\n}\n\nvar controlHandler = {\n ondragmove: function (point: POSITION, index: number, component: Component) {\n var { left, top, width, height } = component.model\n\n var transcoorded = component.transcoordP2S(point.x, point.y)\n var round = ((transcoorded.x - left) / (width / 2)) * 100\n\n round = safeRound(round, width, height)\n\n component.set({\n round\n })\n }\n}\n\n@sceneComponent('ZoneCapacityBar')\nexport default class ZoneCapacityBar extends MCSZoneMixin(MCSUnit) {\n static get nature() {\n return NATURE\n }\n\n get controls() {\n var { left, top, width, round, height } = this.state\n round = round == undefined ? 0 : safeRound(round, width, height)\n\n return [\n {\n x: left + (width / 2) * (round / 100),\n y: top,\n handler: controlHandler\n }\n ]\n }\n\n getLegendFallback() {\n return (this.root as any).zoneCapacityLegendTheme || LEGEND_CAPACITY\n }\n\n get statusColor() {\n const { fullup } = this.state\n return fullup ? 'red' : getValueOnRanges(this.usage, this.legend)\n }\n\n get text() {\n return this.usage + '%'\n }\n\n render(context: CanvasRenderingContext2D) {\n const { left, top, width, height } = this.bounds\n\n if (width >= height) {\n this.renderLandscape(context)\n } else {\n this.renderPortrait(context)\n }\n }\n\n renderLandscape(context: CanvasRenderingContext2D) {\n const { left, top, width, height } = this.bounds\n const { highWatermark, lowWatermark, lineWidth, strokeStyle, round, fillStyle = 'white' } = this.state\n\n context.save()\n context.translate(left, top)\n\n // gauge background\n context.beginPath()\n\n context.fillStyle = fillStyle\n context.roundRect(0, 0, width, height, round)\n\n context.fill()\n context.stroke()\n\n context.clip()\n\n // gauge foreground\n context.beginPath()\n\n context.fillStyle = this.statusColor!\n\n context.rect(0, 0, (width / 100) * this.usage, height)\n context.fill()\n\n // high-watermark level\n\n if (highWatermark && highWatermark > 0 && highWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const x = (width / 100) * highWatermark\n context.moveTo(x, 0)\n context.lineTo(x, height)\n\n context.stroke()\n }\n\n // low-watermark level\n\n if (lowWatermark && lowWatermark > 0 && lowWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const x = (width / 100) * lowWatermark\n context.moveTo(x, 0)\n context.lineTo(x, height)\n\n context.stroke()\n }\n\n // gauge outline\n context.beginPath()\n\n context.setLineDash([])\n context.strokeStyle = strokeStyle\n context.lineWidth = lineWidth\n context.roundRect(0, 0, width, height, round)\n context.stroke()\n\n context.beginPath()\n\n context.translate(-left, -top)\n context.restore()\n }\n\n renderPortrait(context: CanvasRenderingContext2D) {\n const { left, top, width, height } = this.bounds\n const { highWatermark, lowWatermark, lineWidth, strokeStyle, round, fillStyle = 'white' } = this.state\n\n context.save()\n context.translate(left, top)\n\n // gauge background\n context.beginPath()\n\n context.fillStyle = fillStyle\n context.roundRect(0, 0, width, height, round)\n\n context.fill()\n\n context.clip()\n\n // gauge foreground\n context.beginPath()\n\n context.fillStyle = this.statusColor!\n\n context.rect(0, height - (height / 100) * this.usage, width, (height / 100) * this.usage)\n context.fill()\n\n // high-watermark level\n\n if (highWatermark && highWatermark > 0 && highWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const y = height - (height / 100) * highWatermark\n context.moveTo(0, y)\n context.lineTo(width, y)\n\n context.stroke()\n }\n\n // low-watermark level\n\n if (lowWatermark && lowWatermark > 0 && lowWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const y = height - (height / 100) * lowWatermark\n context.moveTo(0, y)\n context.lineTo(width, y)\n\n context.stroke()\n }\n\n // gauge outline\n context.beginPath()\n\n context.setLineDash([])\n context.strokeStyle = strokeStyle\n context.lineWidth = lineWidth\n context.roundRect(0, 0, width, height, round)\n context.stroke()\n\n context.beginPath()\n\n context.translate(-left, -top)\n context.restore()\n }\n\n get usage() {\n const { currentUsage = 0, maxCapacity = 100 } = this.state\n return Math.round((currentUsage / maxCapacity) * 100)\n }\n\n set usage(usage: number) {\n // intentionally have done nothing\n }\n\n onchangeData(after: Record<string, any>, before: Record<string, any>): void {\n const {\n CURRENTCAPACITY = 0,\n MAXCAPACITY = 100,\n HIGHWATERMARK = 100,\n LOWWATERMARK = 0,\n FULLUP = 'F'\n } = this.data && typeof this.data == 'object' ? this.data : {}\n\n const currentUsage = Number(CURRENTCAPACITY) || 0\n const maxCapacity = Number(MAXCAPACITY) || 100\n const highWatermark = Number(HIGHWATERMARK) || 100\n const lowWatermark = Number(LOWWATERMARK) || 0\n\n this.setState({\n currentUsage,\n maxCapacity,\n highWatermark,\n lowWatermark,\n fullup: FULLUP == 'T',\n usage: Math.round((currentUsage / maxCapacity) * 100)\n })\n }\n}\n"]}
Binary file
Binary file
package/jest.config.js ADDED
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ rootDir: '../../',
3
+ roots: ['<rootDir>/packages/machine'],
4
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
5
+ transform: {
6
+ '^.+\\.tsx?$': 'babel-jest'
7
+ },
8
+ testRegex: 'packages/machine/.*\\.test\\.tsx?$',
9
+ testEnvironment: 'node'
10
+ }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@fmsim/machine",
3
3
  "description": "Layout View를 구성하는 설비(machine)들과 유닛을 구현한 모듈입니다.",
4
4
  "author": "heartyoh",
5
- "version": "2.0.0-beta.7",
5
+ "version": "2.0.0-beta.75",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
8
8
  "things-scene": true,
@@ -22,10 +22,11 @@
22
22
  "prepublish": "tsc; exit 0",
23
23
  "lint": "eslint --ext .ts,.html . --ignore-path .gitignore && prettier \"**/*.ts\" --check --ignore-path .gitignore",
24
24
  "format": "eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \"**/*.ts\" --write --ignore-path .gitignore",
25
+ "test": "jest --config jest.config.js",
25
26
  "migration": "things-factory-migration"
26
27
  },
27
28
  "dependencies": {
28
- "@fmsim/api": "^2.0.0-beta.7",
29
+ "@fmsim/api": "^2.0.0-beta.75",
29
30
  "@hatiolab/things-scene": "^10.0.0-beta.1"
30
31
  },
31
32
  "devDependencies": {
@@ -54,5 +55,5 @@
54
55
  "prettier --write"
55
56
  ]
56
57
  },
57
- "gitHead": "ccd94cd6a7157852b9a1d4f59b2f0d46c9b6aac7"
58
+ "gitHead": "1ce494dc2a5005b145f05a2f7f5b253b4a079f96"
58
59
  }
package/src/agv.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ComponentNature, sceneComponent } from '@hatiolab/things-scene'
2
2
  import { themesColorRange } from '@fmsim/api'
3
3
  import MCSVehicle from './mcs-vehicle'
4
- import { getVaueOnRanges } from './utils/get-value-on-ranges'
4
+ import { getValueOnRanges } from './utils/get-value-on-ranges'
5
5
  import { BATTERY_RATE_LEGEND } from './features/mcs-status-default'
6
6
 
7
7
  const NATURE: ComponentNature = {
@@ -50,7 +50,7 @@ export default class AGV extends MCSVehicle {
50
50
  }
51
51
 
52
52
  getBatteryColor(rate: number) {
53
- return getVaueOnRanges(rate, this.getBatteryLegendTheme() || this.getBatteryLegendFallback())
53
+ return getValueOnRanges(rate, this.getBatteryLegendTheme() || this.getBatteryLegendFallback())
54
54
  }
55
55
 
56
56
  render(ctx: CanvasRenderingContext2D) {
@@ -0,0 +1,117 @@
1
+ import { Model } from '@hatiolab/things-scene'
2
+ import type Carrier from './carrier'
3
+ import type MCSCarrierHolder from './mcs-carrier-holder'
4
+
5
+ const DYING_TIMEOUT = 1000
6
+
7
+ export default class CarrierManager {
8
+ private carriers: Map<string, Carrier> = new Map()
9
+ private dyingTimers: Map<string, ReturnType<typeof setTimeout>> = new Map()
10
+ private root: any
11
+
12
+ constructor(root: any) {
13
+ this.root = root
14
+ }
15
+
16
+ get(carrierName: string): Carrier | undefined {
17
+ return this.carriers.get(carrierName)
18
+ }
19
+
20
+ update(holder: MCSCarrierHolder, after: Record<string, any>, before: Record<string, any>): void {
21
+ const afterName = after?.CARRIERNAME
22
+ const beforeName = before?.CARRIERNAME
23
+
24
+ // 이전에는 캐리어가 있었는데 새 데이터에는 없다 → dying 후보
25
+ if (beforeName && (beforeName !== afterName || !afterName)) {
26
+ const carrier = this.carriers.get(beforeName)
27
+ if (carrier && carrier.parent === holder) {
28
+ this.scheduleDying(beforeName)
29
+ }
30
+ return
31
+ }
32
+
33
+ if (!afterName) {
34
+ return
35
+ }
36
+
37
+ // dying 상태면 구출
38
+ const wasDying = this.dyingTimers.has(afterName)
39
+ this.cancelDying(afterName)
40
+
41
+ const { width, height } = holder.state
42
+ let carrier = this.carriers.get(afterName)
43
+
44
+ if (!carrier) {
45
+ // 새로 생성
46
+ carrier = Model.compile(
47
+ {
48
+ id: afterName,
49
+ type: 'Carrier',
50
+ left: 0,
51
+ top: 0,
52
+ width,
53
+ height,
54
+ showText: holder.getState('showCarrierText') || false
55
+ },
56
+ this.root.app
57
+ ) as unknown as Carrier
58
+
59
+ this.carriers.set(afterName, carrier)
60
+ carrier.data = after
61
+ holder.addComponent(carrier, { silent: true })
62
+ } else if (carrier.parent !== holder && (!beforeName && afterName)) {
63
+ // CARRIERNAME이 새로 나타남 (이전엔 없었음) → 다른 holder로 이동
64
+ ;(holder as any).reparent(carrier, { animated: true, duration: 2000, ease: 'inout' })
65
+ }
66
+
67
+ if (carrier.parent === holder) {
68
+ carrier.data = after
69
+ }
70
+ }
71
+
72
+ private scheduleDying(carrierName: string): void {
73
+ this.cancelDying(carrierName)
74
+
75
+ const timer = setTimeout(() => {
76
+ this.dyingTimers.delete(carrierName)
77
+
78
+ const carrier = this.carriers.get(carrierName)
79
+ if (carrier) {
80
+ this.carriers.delete(carrierName)
81
+ if (!carrier.disposed) {
82
+ carrier.removeSelf(true)
83
+ }
84
+ }
85
+ }, DYING_TIMEOUT)
86
+
87
+ this.dyingTimers.set(carrierName, timer)
88
+ }
89
+
90
+ private cancelDying(carrierName: string): void {
91
+ const timer = this.dyingTimers.get(carrierName)
92
+ if (timer) {
93
+ clearTimeout(timer)
94
+ this.dyingTimers.delete(carrierName)
95
+ }
96
+ }
97
+
98
+ onCarrierDisposed(carrierName: string): void {
99
+ this.cancelDying(carrierName)
100
+ this.carriers.delete(carrierName)
101
+ }
102
+
103
+ dispose(): void {
104
+ for (const timer of this.dyingTimers.values()) {
105
+ clearTimeout(timer)
106
+ }
107
+ this.dyingTimers.clear()
108
+
109
+ for (const carrier of this.carriers.values()) {
110
+ if (!carrier.disposed) {
111
+ carrier.removeSelf(true)
112
+ }
113
+ }
114
+
115
+ this.carriers.clear()
116
+ }
117
+ }
package/src/carrier.ts CHANGED
@@ -13,6 +13,20 @@ const NATURE: ComponentNature = {
13
13
  rotatable: false
14
14
  }
15
15
 
16
+ /**
17
+ * Feature flag — keep status text (F / E / X) screen-axis-aligned even
18
+ * when the carrier (or its parent holder, or an in-flight reparent
19
+ * tween) is rotated. Counter-rotates ONLY the text; the polygon path
20
+ * still rotates with the carrier as before.
21
+ *
22
+ * ROLLBACK procedure (if customer decides to revert):
23
+ * 1. Flip this flag to `false` — text rotates with carrier again.
24
+ * 2. Or delete this constant and the — fully strip the feature: keep
25
+ * `if (STATUS_TEXT_COUNTER_ROTATE)` only the `else` branch in
26
+ * branch in `render()`. `render()` (the original code).
27
+ */
28
+ const STATUS_TEXT_COUNTER_ROTATE = true
29
+
16
30
  @sceneComponent('Carrier')
17
31
  export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
18
32
  static get nature() {
@@ -37,6 +51,40 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
37
51
 
38
52
  set path(path) {}
39
53
 
54
+
55
+ added(parent: any) {
56
+ super.added(parent)
57
+
58
+ // 새 부모에 맞춰 크기 및 상태 갱신
59
+ if (parent) {
60
+ const { width, height } = parent.state || {}
61
+ if (width && height) {
62
+ this.set({ width, height })
63
+ }
64
+ }
65
+
66
+ this.syncStateFromData()
67
+
68
+ // 3D 오브젝트 최초 생성 (동기적 — reparent()에서 _realObject 필요)
69
+ if ((this.root as any)?.threed && !this._realObject) {
70
+ ;(this.root as any).model_layer?._threeCapability?.addObject(this)
71
+ }
72
+
73
+ // reparent 애니메이션은 things-scene의 reparent()가 처리
74
+ }
75
+
76
+ /**
77
+ * data로부터 carrier 상태를 동기화 (생성 시, 부모 변경 시 공통 사용)
78
+ */
79
+ private syncStateFromData(): void {
80
+ const { CARRIERNAME, CARRIERTYPE = '', EMPTYTYPE, CARRIERSTATUS } = this.data || {}
81
+
82
+ this.setState('id', CARRIERNAME)
83
+ this.setState('EMPTYTYPE', EMPTYTYPE)
84
+ this.setState('CARRIERTYPE', CARRIERTYPE)
85
+ this.setState('CARRIERSTATUS', CARRIERSTATUS)
86
+ }
87
+
40
88
  onchangeData(after: Record<string, any>, before: Record<string, any>): void {
41
89
  const { CARRIERNAME, CARRIERTYPE = '', EMPTYTYPE, CARRIERSTATUS } = this.data || {}
42
90
 
@@ -46,7 +94,7 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
46
94
  this.setState('CARRIERSTATUS', CARRIERSTATUS)
47
95
 
48
96
  // TODO carrierstatus에 따라서 매핑되는 애니메이션 테마 수행
49
- if (after.data.CARRIERSTATUS !== before.data?.CARRIERSTATUS) {
97
+ if (after.data?.CARRIERSTATUS !== before.data?.CARRIERSTATUS) {
50
98
  const { CARRIERSTATUS: lastCarrierStatus } = before.data || {}
51
99
  const lastAnimationConfig = lastCarrierStatus && this.getAnimationConfig(lastCarrierStatus)
52
100
 
@@ -129,8 +177,14 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
129
177
  }
130
178
 
131
179
  // 캐시에 없으면 파싱하고 저장
132
- const theme = (this.root as any)?.style[carrierAnimationName]
180
+ let theme = (this.root as any)?.style?.[carrierAnimationName]
133
181
  if (theme) {
182
+ // theme이 string이면 먼저 파싱
183
+ if (typeof theme === 'string') {
184
+ try { theme = JSON5.parse(theme) } catch { return ANIMATION_DEFAULT }
185
+ }
186
+ if (typeof theme !== 'object' || theme === null) return ANIMATION_DEFAULT
187
+
134
188
  // 문자열 형태의 설정들을 미리 파싱
135
189
  const parsedTheme = {}
136
190
  for (const [status, config] of Object.entries(theme)) {
@@ -244,11 +298,34 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
244
298
  ctx.textAlign = 'center'
245
299
  ctx.textBaseline = 'middle'
246
300
 
247
- ctx.fillText(text, cx - width / 8, cy + height / 8)
301
+ if (STATUS_TEXT_COUNTER_ROTATE) {
302
+ // Counter-rotate so the status label stays screen-axis-aligned
303
+ // even when the carrier (or its parent holder, or an in-flight
304
+ // reparent transition) is rotated. Reading the current cumulative
305
+ // rotation from the canvas transform handles all sources of
306
+ // rotation uniformly (state.rotation, parent transforms,
307
+ // animation tweens) — no need to enumerate them.
308
+ const m = ctx.getTransform()
309
+ const cumulRotation = Math.atan2(m.b, m.a)
310
+ ctx.save()
311
+ ctx.translate(cx, cy)
312
+ ctx.rotate(-cumulRotation)
313
+ ctx.fillText(text, -width / 8, height / 8)
314
+ ctx.restore()
315
+ } else {
316
+ // Original — text rotates with the carrier.
317
+ ctx.fillText(text, cx - width / 8, cy + height / 8)
318
+ }
248
319
  }
249
320
  }
250
321
  }
251
322
 
323
+ dispose(): void {
324
+ ;(this.root as any)?.carrierManager?.onCarrierDisposed(this.state.id)
325
+
326
+ super.dispose()
327
+ }
328
+
252
329
  async detailInfo() {
253
330
  const { type, id } = this.state
254
331
 
@@ -119,8 +119,11 @@ export default class ConveyorJoin extends ConveyorMixin(MCSTransport) {
119
119
  if (normx * normx + normy * normy < 0.25 && ratiox * ratiox + ratioy * ratioy > 0.25) {
120
120
  const angle = normalizeAngle(Math.atan2(-normy, normx) - Math.PI / 2)
121
121
 
122
- if (angle >= startAngle && angle <= endAngle) {
123
- return true
122
+ if (startAngle <= endAngle) {
123
+ if (angle >= startAngle && angle <= endAngle) return true
124
+ } else {
125
+ // wraparound: startAngle > endAngle (예: π → 0)
126
+ if (angle >= startAngle || angle <= endAngle) return true
124
127
  }
125
128
  }
126
129
 
package/src/conveyor.ts CHANGED
@@ -14,6 +14,14 @@ const NATURE: ComponentNature = {
14
14
  options: ['', 'conveyor', 'belt', 'rail']
15
15
  }
16
16
  },
17
+ {
18
+ type: 'select',
19
+ label: 'conveyor-orientation',
20
+ name: 'orientation',
21
+ property: {
22
+ options: ['', 'horizontal', 'vertical']
23
+ }
24
+ },
17
25
  ...MCSTransport.properties
18
26
  ]
19
27
  }
package/src/crane.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Component, ComponentNature, Properties, sceneComponent } from '@hatiolab/things-scene'
1
+ import { Component, ComponentNature, sceneComponent } from '@hatiolab/things-scene'
2
2
  import MCSVehicle from './mcs-vehicle'
3
3
  import { LEGEND_CRANE } from './features/mcs-status-default'
4
4
 
@@ -10,7 +10,7 @@
10
10
  * Based on operato-scene Vehicle3D, adapted for fmsim:
11
11
  * - extends RealObjectGroup (Conveyor3D와 동일 패턴)
12
12
  * - statusColor from MCSStatusMixin
13
- * - 바닥 원점 (y=0~depth)
13
+ * - 볼륨 중심 원점 (things-scene 기본 컨벤션) - mesh 로컬 [-d/2, +d/2]
14
14
  */
15
15
 
16
16
  import * as THREE from 'three'
@@ -30,10 +30,6 @@ export class AGV3D extends RealObjectGroup {
30
30
  return depth || TRANSPORT_SURFACE_HEIGHT
31
31
  }
32
32
 
33
- protected get syncZPosOffset(): number {
34
- return 0
35
- }
36
-
37
33
  private resolveColor(): number {
38
34
  const comp = this.component as any
39
35
 
@@ -41,14 +37,14 @@ export class AGV3D extends RealObjectGroup {
41
37
  // '#F0F0F0'은 상태 미설정 시 기본 fallback — 무시하고 defaultColor 사용
42
38
  try {
43
39
  const sc = comp.statusColor
44
- if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0') return new THREE.Color(sc).getHex()
40
+ if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0' && sc !== 'transparent') return new THREE.Color(sc).getHex()
45
41
  } catch {
46
42
  /* statusColor 미구현 시 무시 */
47
43
  }
48
44
 
49
45
  // 2) state.fillStyle fallback
50
46
  const { fillStyle } = this.component.state
51
- if (fillStyle && typeof fillStyle === 'string') {
47
+ if (fillStyle && typeof fillStyle === 'string' && fillStyle !== 'transparent') {
52
48
  try {
53
49
  return new THREE.Color(fillStyle).getHex()
54
50
  } catch {
@@ -118,7 +114,8 @@ export class AGV3D extends RealObjectGroup {
118
114
  plat.receiveShadow = true
119
115
 
120
116
  this._ownMeshes = [bumper, body, plat]
121
- for (const m of this._ownMeshes) this.object3d.add(m)
117
+ // ownGroup이 center-origin ↔ bottom-up 좌표계 변환을 자동 처리
118
+ for (const m of this._ownMeshes) this.components3D.add(m)
122
119
  }
123
120
 
124
121
  updateDimension() {}
@@ -126,7 +123,7 @@ export class AGV3D extends RealObjectGroup {
126
123
  update() {
127
124
  // 자신의 mesh만 제거 (Carrier 등 자식 컴포넌트 3D 보존)
128
125
  for (const m of this._ownMeshes) {
129
- this.object3d.remove(m)
126
+ this.components3D.remove(m)
130
127
  disposeObject3D(m)
131
128
  }
132
129
  this._ownMeshes = []
@@ -150,10 +147,12 @@ export class AGV3D extends RealObjectGroup {
150
147
 
151
148
  onchange(after: Record<string, unknown>, before: Record<string, unknown>) {
152
149
  if ('width' in after || 'height' in after || 'depth' in after ||
153
- 'fillStyle' in after || 'strokeStyle' in after || 'id' in after) {
150
+ 'fillStyle' in after || 'strokeStyle' in after || 'id' in after ||
151
+ 'EMPTYTYPE' in after || 'CARRIERSTATUS' in after) {
154
152
  this.update()
155
153
  return
156
154
  }
155
+
157
156
  super.onchange(after, before)
158
157
  }
159
158
  }