@things-factory/ops-master 10.0.18

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 (181) hide show
  1. package/.dockerignore +31 -0
  2. package/dist-client/index.d.ts +1 -0
  3. package/dist-client/index.js +2 -0
  4. package/dist-client/index.js.map +1 -0
  5. package/dist-client/pages/master-axes.d.ts +33 -0
  6. package/dist-client/pages/master-axes.js +176 -0
  7. package/dist-client/pages/master-axes.js.map +1 -0
  8. package/dist-client/pages/master-list.d.ts +68 -0
  9. package/dist-client/pages/master-list.js +267 -0
  10. package/dist-client/pages/master-list.js.map +1 -0
  11. package/dist-client/route.d.ts +1 -0
  12. package/dist-client/route.js +8 -0
  13. package/dist-client/route.js.map +1 -0
  14. package/dist-client/tsconfig.tsbuildinfo +1 -0
  15. package/dist-server/index.d.ts +1 -0
  16. package/dist-server/index.js +5 -0
  17. package/dist-server/index.js.map +1 -0
  18. package/dist-server/service/asset/asset-lifecycle.d.ts +83 -0
  19. package/dist-server/service/asset/asset-lifecycle.js +99 -0
  20. package/dist-server/service/asset/asset-lifecycle.js.map +1 -0
  21. package/dist-server/service/asset/asset-master-resolver.d.ts +44 -0
  22. package/dist-server/service/asset/asset-master-resolver.js +140 -0
  23. package/dist-server/service/asset/asset-master-resolver.js.map +1 -0
  24. package/dist-server/service/asset/asset-master.d.ts +21 -0
  25. package/dist-server/service/asset/asset-master.js +25 -0
  26. package/dist-server/service/asset/asset-master.js.map +1 -0
  27. package/dist-server/service/asset/asset-query.d.ts +8 -0
  28. package/dist-server/service/asset/asset-query.js +50 -0
  29. package/dist-server/service/asset/asset-query.js.map +1 -0
  30. package/dist-server/service/asset/index.d.ts +6 -0
  31. package/dist-server/service/asset/index.js +10 -0
  32. package/dist-server/service/asset/index.js.map +1 -0
  33. package/dist-server/service/asset/physical-asset-class.d.ts +34 -0
  34. package/dist-server/service/asset/physical-asset-class.js +104 -0
  35. package/dist-server/service/asset/physical-asset-class.js.map +1 -0
  36. package/dist-server/service/asset/physical-asset.d.ts +53 -0
  37. package/dist-server/service/asset/physical-asset.js +123 -0
  38. package/dist-server/service/asset/physical-asset.js.map +1 -0
  39. package/dist-server/service/equipment/equipment-class.d.ts +34 -0
  40. package/dist-server/service/equipment/equipment-class.js +104 -0
  41. package/dist-server/service/equipment/equipment-class.js.map +1 -0
  42. package/dist-server/service/equipment/equipment-master-resolver.d.ts +60 -0
  43. package/dist-server/service/equipment/equipment-master-resolver.js +200 -0
  44. package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -0
  45. package/dist-server/service/equipment/equipment-master.d.ts +29 -0
  46. package/dist-server/service/equipment/equipment-master.js +36 -0
  47. package/dist-server/service/equipment/equipment-master.js.map +1 -0
  48. package/dist-server/service/equipment/equipment-query.d.ts +8 -0
  49. package/dist-server/service/equipment/equipment-query.js +50 -0
  50. package/dist-server/service/equipment/equipment-query.js.map +1 -0
  51. package/dist-server/service/equipment/equipment.d.ts +45 -0
  52. package/dist-server/service/equipment/equipment.js +133 -0
  53. package/dist-server/service/equipment/equipment.js.map +1 -0
  54. package/dist-server/service/equipment/index.d.ts +8 -0
  55. package/dist-server/service/equipment/index.js +12 -0
  56. package/dist-server/service/equipment/index.js.map +1 -0
  57. package/dist-server/service/equipment/state-period-rules.d.ts +76 -0
  58. package/dist-server/service/equipment/state-period-rules.js +88 -0
  59. package/dist-server/service/equipment/state-period-rules.js.map +1 -0
  60. package/dist-server/service/equipment/stop-reason-query.d.ts +11 -0
  61. package/dist-server/service/equipment/stop-reason-query.js +39 -0
  62. package/dist-server/service/equipment/stop-reason-query.js.map +1 -0
  63. package/dist-server/service/equipment/stop-reason.d.ts +58 -0
  64. package/dist-server/service/equipment/stop-reason.js +114 -0
  65. package/dist-server/service/equipment/stop-reason.js.map +1 -0
  66. package/dist-server/service/index.d.ts +46 -0
  67. package/dist-server/service/index.js +80 -0
  68. package/dist-server/service/index.js.map +1 -0
  69. package/dist-server/service/location/index.d.ts +5 -0
  70. package/dist-server/service/location/index.js +9 -0
  71. package/dist-server/service/location/index.js.map +1 -0
  72. package/dist-server/service/location/location-master-resolver.d.ts +28 -0
  73. package/dist-server/service/location/location-master-resolver.js +80 -0
  74. package/dist-server/service/location/location-master-resolver.js.map +1 -0
  75. package/dist-server/service/location/location-master.d.ts +24 -0
  76. package/dist-server/service/location/location-master.js +27 -0
  77. package/dist-server/service/location/location-master.js.map +1 -0
  78. package/dist-server/service/location/location-query.d.ts +7 -0
  79. package/dist-server/service/location/location-query.js +41 -0
  80. package/dist-server/service/location/location-query.js.map +1 -0
  81. package/dist-server/service/location/operational-location.d.ts +52 -0
  82. package/dist-server/service/location/operational-location.js +132 -0
  83. package/dist-server/service/location/operational-location.js.map +1 -0
  84. package/dist-server/service/master-axis.d.ts +29 -0
  85. package/dist-server/service/master-axis.js +39 -0
  86. package/dist-server/service/master-axis.js.map +1 -0
  87. package/dist-server/service/master-crud.d.ts +44 -0
  88. package/dist-server/service/master-crud.js +73 -0
  89. package/dist-server/service/master-crud.js.map +1 -0
  90. package/dist-server/service/master-list.d.ts +12 -0
  91. package/dist-server/service/master-list.js +24 -0
  92. package/dist-server/service/master-list.js.map +1 -0
  93. package/dist-server/service/material/index.d.ts +6 -0
  94. package/dist-server/service/material/index.js +10 -0
  95. package/dist-server/service/material/index.js.map +1 -0
  96. package/dist-server/service/material/material-class.d.ts +34 -0
  97. package/dist-server/service/material/material-class.js +104 -0
  98. package/dist-server/service/material/material-class.js.map +1 -0
  99. package/dist-server/service/material/material-definition.d.ts +42 -0
  100. package/dist-server/service/material/material-definition.js +112 -0
  101. package/dist-server/service/material/material-definition.js.map +1 -0
  102. package/dist-server/service/material/material-master-resolver.d.ts +44 -0
  103. package/dist-server/service/material/material-master-resolver.js +140 -0
  104. package/dist-server/service/material/material-master-resolver.js.map +1 -0
  105. package/dist-server/service/material/material-master.d.ts +9 -0
  106. package/dist-server/service/material/material-master.js +24 -0
  107. package/dist-server/service/material/material-master.js.map +1 -0
  108. package/dist-server/service/material/material-query.d.ts +8 -0
  109. package/dist-server/service/material/material-query.js +50 -0
  110. package/dist-server/service/material/material-query.js.map +1 -0
  111. package/dist-server/service/operation/index.d.ts +5 -0
  112. package/dist-server/service/operation/index.js +9 -0
  113. package/dist-server/service/operation/index.js.map +1 -0
  114. package/dist-server/service/operation/operation-definition.d.ts +52 -0
  115. package/dist-server/service/operation/operation-definition.js +142 -0
  116. package/dist-server/service/operation/operation-definition.js.map +1 -0
  117. package/dist-server/service/operation/operation-master-resolver.d.ts +28 -0
  118. package/dist-server/service/operation/operation-master-resolver.js +80 -0
  119. package/dist-server/service/operation/operation-master-resolver.js.map +1 -0
  120. package/dist-server/service/operation/operation-master.d.ts +21 -0
  121. package/dist-server/service/operation/operation-master.js +28 -0
  122. package/dist-server/service/operation/operation-master.js.map +1 -0
  123. package/dist-server/service/operation/operation-query.d.ts +7 -0
  124. package/dist-server/service/operation/operation-query.js +41 -0
  125. package/dist-server/service/operation/operation-query.js.map +1 -0
  126. package/dist-server/service/personnel/index.d.ts +6 -0
  127. package/dist-server/service/personnel/index.js +10 -0
  128. package/dist-server/service/personnel/index.js.map +1 -0
  129. package/dist-server/service/personnel/person.d.ts +51 -0
  130. package/dist-server/service/personnel/person.js +119 -0
  131. package/dist-server/service/personnel/person.js.map +1 -0
  132. package/dist-server/service/personnel/personnel-class.d.ts +54 -0
  133. package/dist-server/service/personnel/personnel-class.js +114 -0
  134. package/dist-server/service/personnel/personnel-class.js.map +1 -0
  135. package/dist-server/service/personnel/personnel-master-resolver.d.ts +44 -0
  136. package/dist-server/service/personnel/personnel-master-resolver.js +140 -0
  137. package/dist-server/service/personnel/personnel-master-resolver.js.map +1 -0
  138. package/dist-server/service/personnel/personnel-master.d.ts +39 -0
  139. package/dist-server/service/personnel/personnel-master.js +34 -0
  140. package/dist-server/service/personnel/personnel-master.js.map +1 -0
  141. package/dist-server/service/personnel/personnel-query.d.ts +21 -0
  142. package/dist-server/service/personnel/personnel-query.js +57 -0
  143. package/dist-server/service/personnel/personnel-query.js.map +1 -0
  144. package/dist-server/service/personnel/qualification.d.ts +97 -0
  145. package/dist-server/service/personnel/qualification.js +122 -0
  146. package/dist-server/service/personnel/qualification.js.map +1 -0
  147. package/dist-server/service/resource/resource-class.d.ts +25 -0
  148. package/dist-server/service/resource/resource-class.js +29 -0
  149. package/dist-server/service/resource/resource-class.js.map +1 -0
  150. package/dist-server/service/status/axis-stamp.d.ts +16 -0
  151. package/dist-server/service/status/axis-stamp.js +22 -0
  152. package/dist-server/service/status/axis-stamp.js.map +1 -0
  153. package/dist-server/service/status/index.d.ts +3 -0
  154. package/dist-server/service/status/index.js +7 -0
  155. package/dist-server/service/status/index.js.map +1 -0
  156. package/dist-server/service/status/master-status.d.ts +45 -0
  157. package/dist-server/service/status/master-status.js +129 -0
  158. package/dist-server/service/status/master-status.js.map +1 -0
  159. package/dist-server/service/test-spec/index.d.ts +5 -0
  160. package/dist-server/service/test-spec/index.js +9 -0
  161. package/dist-server/service/test-spec/index.js.map +1 -0
  162. package/dist-server/service/test-spec/test-spec-master-resolver.d.ts +28 -0
  163. package/dist-server/service/test-spec/test-spec-master-resolver.js +80 -0
  164. package/dist-server/service/test-spec/test-spec-master-resolver.js.map +1 -0
  165. package/dist-server/service/test-spec/test-spec-master.d.ts +6 -0
  166. package/dist-server/service/test-spec/test-spec-master.js +18 -0
  167. package/dist-server/service/test-spec/test-spec-master.js.map +1 -0
  168. package/dist-server/service/test-spec/test-spec-query.d.ts +7 -0
  169. package/dist-server/service/test-spec/test-spec-query.js +41 -0
  170. package/dist-server/service/test-spec/test-spec-query.js.map +1 -0
  171. package/dist-server/service/test-spec/test-specification.d.ts +41 -0
  172. package/dist-server/service/test-spec/test-specification.js +106 -0
  173. package/dist-server/service/test-spec/test-specification.js.map +1 -0
  174. package/dist-server/tsconfig.tsbuildinfo +1 -0
  175. package/package.json +50 -0
  176. package/things-factory.config.js +12 -0
  177. package/translations/en.json +53 -0
  178. package/translations/ja.json +1 -0
  179. package/translations/ko.json +53 -0
  180. package/translations/ms.json +1 -0
  181. package/translations/zh.json +1 -0
@@ -0,0 +1,29 @@
1
+ import type { ResourceClassDef } from '@operato/ops-contract';
2
+ import type { Equipment } from './equipment';
3
+ import type { EquipmentClass } from './equipment-class';
4
+ /** 트윈 `ReferenceMaster.equipment` 의 한 항목. */
5
+ export interface EquipmentEntry {
6
+ id: string;
7
+ kind: string;
8
+ homeLocation: string;
9
+ identity?: string;
10
+ mtbfMs?: number;
11
+ mttrMs?: number;
12
+ workCalendar?: unknown[];
13
+ properties?: unknown[];
14
+ testSpecificationIds?: string[];
15
+ testResults?: unknown[];
16
+ effectiveStart?: string;
17
+ effectiveEnd?: string;
18
+ }
19
+ export declare function toEquipmentEntry(equipment: Equipment): EquipmentEntry;
20
+ /**
21
+ * 설비 축 전체 — 트윈이 당겨 갈 조각.
22
+ *
23
+ * 설비가 자기 등급과 자리를 가리키므로 등급과 함께 나가야 한다. 한 건씩 밀면 설비가 먼저 도착하고
24
+ * 등급이 나중에 오는 순간이 생긴다.
25
+ */
26
+ export declare function equipmentMaster(classes: EquipmentClass[], equipment: Equipment[]): {
27
+ equipmentClasses: ResourceClassDef[];
28
+ equipment: EquipmentEntry[];
29
+ };
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toEquipmentEntry = toEquipmentEntry;
4
+ exports.equipmentMaster = equipmentMaster;
5
+ const resource_class_1 = require("../resource/resource-class");
6
+ function toEquipmentEntry(equipment) {
7
+ /* `kind` 와 `homeLocation` 은 비울 수 없다 — 자리를 모르면 커널이 어디서 일하는지 정할 수 없다. */
8
+ const out = {
9
+ id: equipment.name,
10
+ kind: equipment.kind,
11
+ homeLocation: equipment.homeLocation
12
+ };
13
+ (0, resource_class_1.put)(out, 'identity', equipment.identity);
14
+ (0, resource_class_1.put)(out, 'mtbfMs', equipment.mtbfMs === undefined || equipment.mtbfMs === null ? undefined : Number(equipment.mtbfMs));
15
+ (0, resource_class_1.put)(out, 'mttrMs', equipment.mttrMs === undefined || equipment.mttrMs === null ? undefined : Number(equipment.mttrMs));
16
+ (0, resource_class_1.put)(out, 'workCalendar', equipment.workCalendar);
17
+ (0, resource_class_1.put)(out, 'properties', equipment.properties);
18
+ (0, resource_class_1.put)(out, 'testSpecificationIds', equipment.testSpecificationIds);
19
+ (0, resource_class_1.put)(out, 'testResults', equipment.testResults);
20
+ (0, resource_class_1.put)(out, 'effectiveStart', (0, resource_class_1.iso)(equipment.effectiveStart));
21
+ (0, resource_class_1.put)(out, 'effectiveEnd', (0, resource_class_1.iso)(equipment.effectiveEnd));
22
+ return out;
23
+ }
24
+ /**
25
+ * 설비 축 전체 — 트윈이 당겨 갈 조각.
26
+ *
27
+ * 설비가 자기 등급과 자리를 가리키므로 등급과 함께 나가야 한다. 한 건씩 밀면 설비가 먼저 도착하고
28
+ * 등급이 나중에 오는 순간이 생긴다.
29
+ */
30
+ function equipmentMaster(classes, equipment) {
31
+ return {
32
+ equipmentClasses: classes.map(resource_class_1.toResourceClassDef),
33
+ equipment: equipment.map(toEquipmentEntry)
34
+ };
35
+ }
36
+ //# sourceMappingURL=equipment-master.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"equipment-master.js","sourceRoot":"","sources":["../../../server/service/equipment/equipment-master.ts"],"names":[],"mappings":";;AAsBA,4CAmBC;AAQD,0CAQC;AAvDD,+DAAyE;AAoBzE,SAAgB,gBAAgB,CAAC,SAAoB;IACnD,uEAAuE;IACvE,MAAM,GAAG,GAA4B;QACnC,EAAE,EAAE,SAAS,CAAC,IAAI;QAClB,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,YAAY,EAAE,SAAS,CAAC,YAAY;KACrC,CAAA;IAED,IAAA,oBAAG,EAAC,GAAG,EAAE,UAAU,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAA;IACxC,IAAA,oBAAG,EAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,MAAM,KAAK,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;IACtH,IAAA,oBAAG,EAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,MAAM,KAAK,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;IACtH,IAAA,oBAAG,EAAC,GAAG,EAAE,cAAc,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;IAChD,IAAA,oBAAG,EAAC,GAAG,EAAE,YAAY,EAAE,SAAS,CAAC,UAAU,CAAC,CAAA;IAC5C,IAAA,oBAAG,EAAC,GAAG,EAAE,sBAAsB,EAAE,SAAS,CAAC,oBAAoB,CAAC,CAAA;IAChE,IAAA,oBAAG,EAAC,GAAG,EAAE,aAAa,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;IAC9C,IAAA,oBAAG,EAAC,GAAG,EAAE,gBAAgB,EAAE,IAAA,oBAAG,EAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAA;IACzD,IAAA,oBAAG,EAAC,GAAG,EAAE,cAAc,EAAE,IAAA,oBAAG,EAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAA;IAErD,OAAO,GAAgC,CAAA;AACzC,CAAC;AAED;;;;;GAKG;AACH,SAAgB,eAAe,CAC7B,OAAyB,EACzB,SAAsB;IAEtB,OAAO;QACL,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,mCAAkB,CAAC;QACjD,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,gBAAgB,CAAC;KAC3C,CAAA;AACH,CAAC","sourcesContent":["import type { ResourceClassDef } from '@operato/ops-contract'\n\nimport { iso, put, toResourceClassDef } from '../resource/resource-class'\nimport type { Equipment } from './equipment'\nimport type { EquipmentClass } from './equipment-class'\n\n/** 트윈 `ReferenceMaster.equipment` 의 한 항목. */\nexport interface EquipmentEntry {\n id: string\n kind: string\n homeLocation: string\n identity?: string\n mtbfMs?: number\n mttrMs?: number\n workCalendar?: unknown[]\n properties?: unknown[]\n testSpecificationIds?: string[]\n testResults?: unknown[]\n effectiveStart?: string\n effectiveEnd?: string\n}\n\nexport function toEquipmentEntry(equipment: Equipment): EquipmentEntry {\n /* `kind` 와 `homeLocation` 은 비울 수 없다 — 자리를 모르면 커널이 어디서 일하는지 정할 수 없다. */\n const out: Record<string, unknown> = {\n id: equipment.name,\n kind: equipment.kind,\n homeLocation: equipment.homeLocation\n }\n\n put(out, 'identity', equipment.identity)\n put(out, 'mtbfMs', equipment.mtbfMs === undefined || equipment.mtbfMs === null ? undefined : Number(equipment.mtbfMs))\n put(out, 'mttrMs', equipment.mttrMs === undefined || equipment.mttrMs === null ? undefined : Number(equipment.mttrMs))\n put(out, 'workCalendar', equipment.workCalendar)\n put(out, 'properties', equipment.properties)\n put(out, 'testSpecificationIds', equipment.testSpecificationIds)\n put(out, 'testResults', equipment.testResults)\n put(out, 'effectiveStart', iso(equipment.effectiveStart))\n put(out, 'effectiveEnd', iso(equipment.effectiveEnd))\n\n return out as unknown as EquipmentEntry\n}\n\n/**\n * 설비 축 전체 — 트윈이 당겨 갈 조각.\n *\n * 설비가 자기 등급과 자리를 가리키므로 등급과 함께 나가야 한다. 한 건씩 밀면 설비가 먼저 도착하고\n * 등급이 나중에 오는 순간이 생긴다.\n */\nexport function equipmentMaster(\n classes: EquipmentClass[],\n equipment: Equipment[]\n): { equipmentClasses: ResourceClassDef[]; equipment: EquipmentEntry[] } {\n return {\n equipmentClasses: classes.map(toResourceClassDef),\n equipment: equipment.map(toEquipmentEntry)\n }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ export declare class EquipmentMaster {
2
+ equipmentClasses: string;
3
+ equipment: string;
4
+ }
5
+ export declare class EquipmentQuery {
6
+ /** 설비 축 전체를 한 번에 낸다 — 설비가 자기 등급과 자리를 가리키므로 함께 나가야 한다. */
7
+ equipmentMaster(context: ResolverContext): Promise<EquipmentMaster>;
8
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EquipmentQuery = exports.EquipmentMaster = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const type_graphql_1 = require("type-graphql");
6
+ const shell_1 = require("@things-factory/shell");
7
+ const equipment_1 = require("./equipment");
8
+ const equipment_class_1 = require("./equipment-class");
9
+ const equipment_master_1 = require("./equipment-master");
10
+ let EquipmentMaster = class EquipmentMaster {
11
+ };
12
+ exports.EquipmentMaster = EquipmentMaster;
13
+ tslib_1.__decorate([
14
+ (0, type_graphql_1.Field)(type => String, { description: 'ResourceClassDef[] as JSON text' }),
15
+ tslib_1.__metadata("design:type", String)
16
+ ], EquipmentMaster.prototype, "equipmentClasses", void 0);
17
+ tslib_1.__decorate([
18
+ (0, type_graphql_1.Field)(type => String, { description: 'ReferenceMaster equipment[] as JSON text' }),
19
+ tslib_1.__metadata("design:type", String)
20
+ ], EquipmentMaster.prototype, "equipment", void 0);
21
+ exports.EquipmentMaster = EquipmentMaster = tslib_1.__decorate([
22
+ (0, type_graphql_1.ObjectType)({ description: 'Equipment master as the twin receives it' })
23
+ ], EquipmentMaster);
24
+ let EquipmentQuery = class EquipmentQuery {
25
+ /** 설비 축 전체를 한 번에 낸다 — 설비가 자기 등급과 자리를 가리키므로 함께 나가야 한다. */
26
+ async equipmentMaster(context) {
27
+ const { domain } = context.state;
28
+ const [classes, equipment] = await Promise.all([
29
+ (0, shell_1.getRepository)(equipment_class_1.EquipmentClass).find({ where: { domain: { id: domain.id } }, order: { name: 'ASC' } }),
30
+ (0, shell_1.getRepository)(equipment_1.Equipment).find({ where: { domain: { id: domain.id } }, order: { name: 'ASC' } })
31
+ ]);
32
+ const master = (0, equipment_master_1.equipmentMaster)(classes, equipment);
33
+ return {
34
+ equipmentClasses: JSON.stringify(master.equipmentClasses),
35
+ equipment: JSON.stringify(master.equipment)
36
+ };
37
+ }
38
+ };
39
+ exports.EquipmentQuery = EquipmentQuery;
40
+ tslib_1.__decorate([
41
+ (0, type_graphql_1.Query)(returns => EquipmentMaster, { description: 'Equipment master for the twin to pull' }),
42
+ tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
43
+ tslib_1.__metadata("design:type", Function),
44
+ tslib_1.__metadata("design:paramtypes", [Object]),
45
+ tslib_1.__metadata("design:returntype", Promise)
46
+ ], EquipmentQuery.prototype, "equipmentMaster", null);
47
+ exports.EquipmentQuery = EquipmentQuery = tslib_1.__decorate([
48
+ (0, type_graphql_1.Resolver)(equipment_1.Equipment)
49
+ ], EquipmentQuery);
50
+ //# sourceMappingURL=equipment-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"equipment-query.js","sourceRoot":"","sources":["../../../server/service/equipment/equipment-query.ts"],"names":[],"mappings":";;;;AAAA,+CAAsE;AAEtE,iDAAqD;AAErD,2CAAuC;AACvC,uDAAkD;AAClD,yDAA4E;AAGrE,IAAM,eAAe,GAArB,MAAM,eAAe;CAM3B,CAAA;AANY,0CAAe;AAE1B;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,iCAAiC,EAAE,CAAC;;yDAClD;AAGxB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC;;kDAClE;0BALN,eAAe;IAD3B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC;GAC3D,eAAe,CAM3B;AAGM,IAAM,cAAc,GAApB,MAAM,cAAc;IACzB,yDAAyD;IAEnD,AAAN,KAAK,CAAC,eAAe,CAAQ,OAAwB;QACnD,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC7C,IAAA,qBAAa,EAAC,gCAAc,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACpG,IAAA,qBAAa,EAAC,qBAAS,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;SAChG,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,IAAA,kCAAoB,EAAC,OAAO,EAAE,SAAS,CAAC,CAAA;QAEvD,OAAO;YACL,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC;YACzD,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC;SAC5C,CAAA;IACH,CAAC;CACF,CAAA;AAlBY,wCAAc;AAGnB;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,eAAe,EAAE,EAAE,WAAW,EAAE,uCAAuC,EAAE,CAAC;IACrE,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;qDAc3B;yBAjBU,cAAc;IAD1B,IAAA,uBAAQ,EAAC,qBAAS,CAAC;GACP,cAAc,CAkB1B","sourcesContent":["import { Ctx, Field, ObjectType, Query, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { Equipment } from './equipment'\nimport { EquipmentClass } from './equipment-class'\nimport { equipmentMaster as buildEquipmentMaster } from './equipment-master'\n\n@ObjectType({ description: 'Equipment master as the twin receives it' })\nexport class EquipmentMaster {\n @Field(type => String, { description: 'ResourceClassDef[] as JSON text' })\n equipmentClasses: string\n\n @Field(type => String, { description: 'ReferenceMaster equipment[] as JSON text' })\n equipment: string\n}\n\n@Resolver(Equipment)\nexport class EquipmentQuery {\n /** 설비 축 전체를 한 번에 낸다 — 설비가 자기 등급과 자리를 가리키므로 함께 나가야 한다. */\n @Query(returns => EquipmentMaster, { description: 'Equipment master for the twin to pull' })\n async equipmentMaster(@Ctx() context: ResolverContext): Promise<EquipmentMaster> {\n const { domain } = context.state\n\n const [classes, equipment] = await Promise.all([\n getRepository(EquipmentClass).find({ where: { domain: { id: domain.id } }, order: { name: 'ASC' } }),\n getRepository(Equipment).find({ where: { domain: { id: domain.id } }, order: { name: 'ASC' } })\n ])\n\n const master = buildEquipmentMaster(classes, equipment)\n\n return {\n equipmentClasses: JSON.stringify(master.equipmentClasses),\n equipment: JSON.stringify(master.equipment)\n }\n }\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import { Domain } from '@things-factory/shell';
2
+ import { User } from '@things-factory/auth-base';
3
+ /**
4
+ * 설비 — **ISA-95 `Equipment`**.
5
+ *
6
+ * 설비는 작업을 수행하는 자원이다. 배정을 받고, 가동률로 평가받고, 능력으로 찾힌다. **계측기는 설비가
7
+ * 아니다** — 저울과 센서는 그 어느 것도 하지 않고, 그것은 관측의 원천이다. 계측기를 설비로 세우면
8
+ * 가동률 화면이 저울을 기계로 세고 「설비가 논다」가 나온다.
9
+ */
10
+ export declare class Equipment {
11
+ readonly id: string;
12
+ domain?: Domain;
13
+ domainId?: string;
14
+ /** 표준 `EquipmentID` — 트윈으로 나갈 때 `equipment[].id` 가 된다. */
15
+ name: string;
16
+ description?: string;
17
+ /** 속한 등급 — 표준 `EquipmentClassID`. 계약의 `kind` 자리다. */
18
+ kind: string;
19
+ /** 이 설비가 있는 자리 — 표준 `OperationalLocation`. */
20
+ homeLocation: string;
21
+ /** GS1 GIAI. 없으면 비워 둔다 — 회사 프리픽스를 지어내지 않는다. */
22
+ identity?: string;
23
+ /** 평균 고장 간격(밀리초) — 없으면 고장이 없는 설비로 예측된다. */
24
+ mtbfMs?: number;
25
+ /** 평균 수리 시간(밀리초). */
26
+ mttrMs?: number;
27
+ /** 근무 캘린더 — 표준 `WorkCalendar`. 교대 이름과 구간. */
28
+ workCalendar?: Record<string, unknown>[];
29
+ /** 개체 속성 — 표준 `EquipmentProperty`. 속도처럼 계산에 들어가는 속성이 여기 온다. */
30
+ properties?: Record<string, unknown>[];
31
+ /** 이 설비에 요구되는 `TestSpecification` 참조들 — 검정·교정이 이 모양에 맞는다. */
32
+ testSpecificationIds?: string[];
33
+ /** `TestResult` — 만료되었거나 떨어졌으면 그 설비에 작업이 가지 않는다. */
34
+ testResults?: Record<string, unknown>[];
35
+ /** 언제부터 이 설비가 현장에 있나 — 도입 예정 설비를 미리 등록할 수 있다. */
36
+ effectiveStart?: Date;
37
+ /** 언제까지 — 폐기한 설비가 영원히 배정 대상으로 남지 않게 한다. */
38
+ effectiveEnd?: Date;
39
+ createdAt?: Date;
40
+ updatedAt?: Date;
41
+ creator?: User;
42
+ creatorId?: string;
43
+ updater?: User;
44
+ updaterId?: string;
45
+ }
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Equipment = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const typeorm_1 = require("typeorm");
6
+ const type_graphql_1 = require("type-graphql");
7
+ const shell_1 = require("@things-factory/shell");
8
+ const auth_base_1 = require("@things-factory/auth-base");
9
+ /**
10
+ * 설비 — **ISA-95 `Equipment`**.
11
+ *
12
+ * 설비는 작업을 수행하는 자원이다. 배정을 받고, 가동률로 평가받고, 능력으로 찾힌다. **계측기는 설비가
13
+ * 아니다** — 저울과 센서는 그 어느 것도 하지 않고, 그것은 관측의 원천이다. 계측기를 설비로 세우면
14
+ * 가동률 화면이 저울을 기계로 세고 「설비가 논다」가 나온다.
15
+ */
16
+ let Equipment = class Equipment {
17
+ };
18
+ exports.Equipment = Equipment;
19
+ tslib_1.__decorate([
20
+ (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
21
+ (0, type_graphql_1.Field)(type => type_graphql_1.ID),
22
+ tslib_1.__metadata("design:type", String)
23
+ ], Equipment.prototype, "id", void 0);
24
+ tslib_1.__decorate([
25
+ (0, typeorm_1.ManyToOne)(type => shell_1.Domain),
26
+ (0, type_graphql_1.Field)(type => shell_1.Domain),
27
+ tslib_1.__metadata("design:type", shell_1.Domain)
28
+ ], Equipment.prototype, "domain", void 0);
29
+ tslib_1.__decorate([
30
+ (0, typeorm_1.RelationId)((equipment) => equipment.domain),
31
+ tslib_1.__metadata("design:type", String)
32
+ ], Equipment.prototype, "domainId", void 0);
33
+ tslib_1.__decorate([
34
+ (0, typeorm_1.Column)(),
35
+ (0, type_graphql_1.Field)(),
36
+ tslib_1.__metadata("design:type", String)
37
+ ], Equipment.prototype, "name", void 0);
38
+ tslib_1.__decorate([
39
+ (0, typeorm_1.Column)({ nullable: true }),
40
+ (0, type_graphql_1.Field)({ nullable: true }),
41
+ tslib_1.__metadata("design:type", String)
42
+ ], Equipment.prototype, "description", void 0);
43
+ tslib_1.__decorate([
44
+ (0, typeorm_1.Column)(),
45
+ (0, type_graphql_1.Field)(),
46
+ tslib_1.__metadata("design:type", String)
47
+ ], Equipment.prototype, "kind", void 0);
48
+ tslib_1.__decorate([
49
+ (0, typeorm_1.Column)(),
50
+ (0, type_graphql_1.Field)(),
51
+ tslib_1.__metadata("design:type", String)
52
+ ], Equipment.prototype, "homeLocation", void 0);
53
+ tslib_1.__decorate([
54
+ (0, typeorm_1.Column)({ nullable: true }),
55
+ (0, type_graphql_1.Field)({ nullable: true }),
56
+ tslib_1.__metadata("design:type", String)
57
+ ], Equipment.prototype, "identity", void 0);
58
+ tslib_1.__decorate([
59
+ (0, typeorm_1.Column)({ type: 'bigint', nullable: true }),
60
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true }),
61
+ tslib_1.__metadata("design:type", Number)
62
+ ], Equipment.prototype, "mtbfMs", void 0);
63
+ tslib_1.__decorate([
64
+ (0, typeorm_1.Column)({ type: 'bigint', nullable: true }),
65
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true }),
66
+ tslib_1.__metadata("design:type", Number)
67
+ ], Equipment.prototype, "mttrMs", void 0);
68
+ tslib_1.__decorate([
69
+ (0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
70
+ (0, type_graphql_1.Field)(type => String, { nullable: true, description: 'WorkCalendarEntry[] as JSON text' }),
71
+ tslib_1.__metadata("design:type", Array)
72
+ ], Equipment.prototype, "workCalendar", void 0);
73
+ tslib_1.__decorate([
74
+ (0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
75
+ (0, type_graphql_1.Field)(type => String, { nullable: true, description: 'ResourceProperty[] as JSON text' }),
76
+ tslib_1.__metadata("design:type", Array)
77
+ ], Equipment.prototype, "properties", void 0);
78
+ tslib_1.__decorate([
79
+ (0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
80
+ (0, type_graphql_1.Field)(type => [String], { nullable: true }),
81
+ tslib_1.__metadata("design:type", Array)
82
+ ], Equipment.prototype, "testSpecificationIds", void 0);
83
+ tslib_1.__decorate([
84
+ (0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
85
+ (0, type_graphql_1.Field)(type => String, { nullable: true, description: 'TestResult[] as JSON text' }),
86
+ tslib_1.__metadata("design:type", Array)
87
+ ], Equipment.prototype, "testResults", void 0);
88
+ tslib_1.__decorate([
89
+ (0, typeorm_1.Column)({ nullable: true }),
90
+ (0, type_graphql_1.Field)({ nullable: true }),
91
+ tslib_1.__metadata("design:type", Date
92
+ /** 언제까지 — 폐기한 설비가 영원히 배정 대상으로 남지 않게 한다. */
93
+ )
94
+ ], Equipment.prototype, "effectiveStart", void 0);
95
+ tslib_1.__decorate([
96
+ (0, typeorm_1.Column)({ nullable: true }),
97
+ (0, type_graphql_1.Field)({ nullable: true }),
98
+ tslib_1.__metadata("design:type", Date)
99
+ ], Equipment.prototype, "effectiveEnd", void 0);
100
+ tslib_1.__decorate([
101
+ (0, typeorm_1.CreateDateColumn)(),
102
+ (0, type_graphql_1.Field)({ nullable: true }),
103
+ tslib_1.__metadata("design:type", Date)
104
+ ], Equipment.prototype, "createdAt", void 0);
105
+ tslib_1.__decorate([
106
+ (0, typeorm_1.UpdateDateColumn)(),
107
+ (0, type_graphql_1.Field)({ nullable: true }),
108
+ tslib_1.__metadata("design:type", Date)
109
+ ], Equipment.prototype, "updatedAt", void 0);
110
+ tslib_1.__decorate([
111
+ (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
112
+ (0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true }),
113
+ tslib_1.__metadata("design:type", auth_base_1.User)
114
+ ], Equipment.prototype, "creator", void 0);
115
+ tslib_1.__decorate([
116
+ (0, typeorm_1.RelationId)((equipment) => equipment.creator),
117
+ tslib_1.__metadata("design:type", String)
118
+ ], Equipment.prototype, "creatorId", void 0);
119
+ tslib_1.__decorate([
120
+ (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
121
+ (0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true }),
122
+ tslib_1.__metadata("design:type", auth_base_1.User)
123
+ ], Equipment.prototype, "updater", void 0);
124
+ tslib_1.__decorate([
125
+ (0, typeorm_1.RelationId)((equipment) => equipment.updater),
126
+ tslib_1.__metadata("design:type", String)
127
+ ], Equipment.prototype, "updaterId", void 0);
128
+ exports.Equipment = Equipment = tslib_1.__decorate([
129
+ (0, typeorm_1.Entity)({ name: 'ops_equipment' }),
130
+ (0, typeorm_1.Index)('ix_ops_equipment_0', (equipment) => [equipment.domain, equipment.name], { unique: true }),
131
+ (0, type_graphql_1.ObjectType)({ description: 'ISA-95 Equipment — a resource that performs work' })
132
+ ], Equipment);
133
+ //# sourceMappingURL=equipment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"equipment.js","sourceRoot":"","sources":["../../../server/service/equipment/equipment.ts"],"names":[],"mappings":";;;;AAAA,qCASgB;AAChB,+CAAyD;AAEzD,iDAA8C;AAC9C,yDAAgD;AAEhD;;;;;;GAMG;AAII,IAAM,SAAS,GAAf,MAAM,SAAS;CAiGrB,CAAA;AAjGY,8BAAS;AAGX;IAFR,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,CAAC;;qCACC;AAInB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IACzB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;sCACb,cAAM;yCAAA;AAGf;IADC,IAAA,oBAAU,EAAC,CAAC,SAAoB,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC;;2CACtC;AAKjB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,GAAE;;uCACI;AAIZ;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CACN;AAKpB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,GAAE;;uCACI;AAKZ;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,GAAE;;+CACY;AAKpB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACT;AAKjB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;yCACxB;AAKf;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;yCACxB;AAKf;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;;+CACnD;AAKxC;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iCAAiC,EAAE,CAAC;;6CACpD;AAKtC;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;uDACb;AAK/B;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,2BAA2B,EAAE,CAAC;;8CAC7C;AAKvC;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCACT,IAAI;IAErB,2CAA2C;;iDAFtB;AAKrB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCACX,IAAI;+CAAA;AAInB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCACd,IAAI;4CAAA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCACd,IAAI;4CAAA;AAIhB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCAC9B,gBAAI;0CAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,SAAoB,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC;;4CACtC;AAIlB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCAC9B,gBAAI;0CAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,SAAoB,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC;;4CACtC;oBAhGP,SAAS;IAHrB,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;IACjC,IAAA,eAAK,EAAC,oBAAoB,EAAE,CAAC,SAAoB,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC3G,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC;GACnE,SAAS,CAiGrB","sourcesContent":["import {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Field, ID, Int, ObjectType } from 'type-graphql'\n\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/**\n * 설비 — **ISA-95 `Equipment`**.\n *\n * 설비는 작업을 수행하는 자원이다. 배정을 받고, 가동률로 평가받고, 능력으로 찾힌다. **계측기는 설비가\n * 아니다** — 저울과 센서는 그 어느 것도 하지 않고, 그것은 관측의 원천이다. 계측기를 설비로 세우면\n * 가동률 화면이 저울을 기계로 세고 「설비가 논다」가 나온다.\n */\n@Entity({ name: 'ops_equipment' })\n@Index('ix_ops_equipment_0', (equipment: Equipment) => [equipment.domain, equipment.name], { unique: true })\n@ObjectType({ description: 'ISA-95 Equipment — a resource that performs work' })\nexport class Equipment {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain)\n domain?: Domain\n\n @RelationId((equipment: Equipment) => equipment.domain)\n domainId?: string\n\n /** 표준 `EquipmentID` — 트윈으로 나갈 때 `equipment[].id` 가 된다. */\n @Column()\n @Field()\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n /** 속한 등급 — 표준 `EquipmentClassID`. 계약의 `kind` 자리다. */\n @Column()\n @Field()\n kind: string\n\n /** 이 설비가 있는 자리 — 표준 `OperationalLocation`. */\n @Column()\n @Field()\n homeLocation: string\n\n /** GS1 GIAI. 없으면 비워 둔다 — 회사 프리픽스를 지어내지 않는다. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n identity?: string\n\n /** 평균 고장 간격(밀리초) — 없으면 고장이 없는 설비로 예측된다. */\n @Column({ type: 'bigint', nullable: true })\n @Field(type => Int, { nullable: true })\n mtbfMs?: number\n\n /** 평균 수리 시간(밀리초). */\n @Column({ type: 'bigint', nullable: true })\n @Field(type => Int, { nullable: true })\n mttrMs?: number\n\n /** 근무 캘린더 — 표준 `WorkCalendar`. 교대 이름과 구간. */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => String, { nullable: true, description: 'WorkCalendarEntry[] as JSON text' })\n workCalendar?: Record<string, unknown>[]\n\n /** 개체 속성 — 표준 `EquipmentProperty`. 속도처럼 계산에 들어가는 속성이 여기 온다. */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => String, { nullable: true, description: 'ResourceProperty[] as JSON text' })\n properties?: Record<string, unknown>[]\n\n /** 이 설비에 요구되는 `TestSpecification` 참조들 — 검정·교정이 이 모양에 맞는다. */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => [String], { nullable: true })\n testSpecificationIds?: string[]\n\n /** `TestResult` — 만료되었거나 떨어졌으면 그 설비에 작업이 가지 않는다. */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => String, { nullable: true, description: 'TestResult[] as JSON text' })\n testResults?: Record<string, unknown>[]\n\n /** 언제부터 이 설비가 현장에 있나 — 도입 예정 설비를 미리 등록할 수 있다. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveStart?: Date\n\n /** 언제까지 — 폐기한 설비가 영원히 배정 대상으로 남지 않게 한다. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveEnd?: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((equipment: Equipment) => equipment.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((equipment: Equipment) => equipment.updater)\n updaterId?: string\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import { Equipment } from './equipment';
2
+ import { EquipmentClass } from './equipment-class';
3
+ import { EquipmentQuery } from './equipment-query';
4
+ import { StopReason } from './stop-reason';
5
+ import { StopReasonQuery } from './stop-reason-query';
6
+ import { EquipmentClassResolver, EquipmentResolver, StopReasonResolver } from './equipment-master-resolver';
7
+ export declare const entities: (typeof Equipment | typeof EquipmentClass | typeof StopReason)[];
8
+ export declare const resolvers: (typeof EquipmentClassResolver | typeof EquipmentResolver | typeof StopReasonResolver | typeof EquipmentQuery | typeof StopReasonQuery)[];
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolvers = exports.entities = void 0;
4
+ const equipment_1 = require("./equipment");
5
+ const equipment_class_1 = require("./equipment-class");
6
+ const equipment_query_1 = require("./equipment-query");
7
+ const stop_reason_1 = require("./stop-reason");
8
+ const stop_reason_query_1 = require("./stop-reason-query");
9
+ const equipment_master_resolver_1 = require("./equipment-master-resolver");
10
+ exports.entities = [equipment_class_1.EquipmentClass, equipment_1.Equipment, stop_reason_1.StopReason];
11
+ exports.resolvers = [equipment_query_1.EquipmentQuery, stop_reason_query_1.StopReasonQuery, equipment_master_resolver_1.EquipmentClassResolver, equipment_master_resolver_1.EquipmentResolver, equipment_master_resolver_1.StopReasonResolver];
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/equipment/index.ts"],"names":[],"mappings":";;;AAAA,2CAAuC;AACvC,uDAAkD;AAClD,uDAAkD;AAClD,+CAA0C;AAC1C,2DAAqD;AACrD,2EAA2G;AAE9F,QAAA,QAAQ,GAAG,CAAC,gCAAc,EAAE,qBAAS,EAAE,wBAAU,CAAC,CAAA;AAClD,QAAA,SAAS,GAAG,CAAC,gCAAc,EAAE,mCAAe,EAAE,kDAAsB,EAAE,6CAAiB,EAAE,8CAAkB,CAAC,CAAA","sourcesContent":["import { Equipment } from './equipment'\nimport { EquipmentClass } from './equipment-class'\nimport { EquipmentQuery } from './equipment-query'\nimport { StopReason } from './stop-reason'\nimport { StopReasonQuery } from './stop-reason-query'\nimport { EquipmentClassResolver, EquipmentResolver, StopReasonResolver } from './equipment-master-resolver'\n\nexport const entities = [EquipmentClass, Equipment, StopReason]\nexport const resolvers = [EquipmentQuery, StopReasonQuery, EquipmentClassResolver, EquipmentResolver, StopReasonResolver]\n"]}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * 상태 구간을 받아도 되나 — **순수 판단.**
3
+ *
4
+ * ── 왜 규칙이 필요한가 ──────────────────────────────────────────────────────
5
+ * 구간은 사람이 되돌아가 적는다. 그래서 겹치고, 거꾸로 되고, 미래로 간다. 그대로 받으면 가동률의
6
+ * 분자가 분모보다 커지는 날이 온다. 그때는 이미 잘못된 구간이 수백 개 쌓여 있다.
7
+ *
8
+ * ── 왜 사유와 상태를 맞춰 보나 ──────────────────────────────────────────────
9
+ * ISO 22400 에서 가동률을 깎느냐 마느냐는 **상태**가 정한다. 「점심시간」을 `down` 으로 적으면 가동률이
10
+ * 오류 보고 없이 낮아지고, 「고장」을 `planned-stop` 으로 적으면 오류 보고 없이 높아진다. 둘 다 조용한
11
+ * 거짓이라 나중에 찾을 방법이 없다.
12
+ */
13
+ /** 계약의 `EQUIPMENT_STATUS` 와 같은 어휘. 여기서 새로 만들지 않는다. */
14
+ export declare const EQUIPMENT_PERIOD_STATUS: readonly ["idle", "busy", "down", "setup", "planned-stop"];
15
+ export type EquipmentPeriodStatus = (typeof EQUIPMENT_PERIOD_STATUS)[number];
16
+ export type PeriodProblem =
17
+ /** 끝이 시작보다 앞이거나 같다 — 길이가 없는 구간은 셀 것이 없다. */
18
+ 'not-a-period'
19
+ /** 아직 오지 않은 시각이다. */
20
+ | 'in-the-future'
21
+ /** 같은 설비의 다른 구간과 겹친다 — 한 설비가 같은 시각에 두 상태일 수 없다. */
22
+ | 'overlaps'
23
+ /** 계약이 모르는 상태다. */
24
+ | 'unknown-status'
25
+ /** 사유가 사유 마스터에 없다. */
26
+ | 'unknown-reason'
27
+ /** 사유가 이 상태의 사유가 아니다 — 가동률이 오류 보고 없이 틀어진다. */
28
+ | 'reason-status-mismatch'
29
+ /** 이 사유는 상세를 함께 적어야 한다. */
30
+ | 'detail-required';
31
+ export interface PeriodIssue {
32
+ problem: PeriodProblem;
33
+ /** 겹치는 것이면 어느 구간과 겹치나(시작 시각, ISO). */
34
+ conflictsWith?: string;
35
+ /** 사유 문제이면 어느 사유인가. */
36
+ reasonCode?: string;
37
+ /** 사유가 속한 상태와 적힌 상태. */
38
+ reasonStatus?: string;
39
+ status?: string;
40
+ }
41
+ export interface JudgeablePeriod {
42
+ status: string;
43
+ startTime: Date | string;
44
+ endTime: Date | string;
45
+ reasonCode?: string;
46
+ reasonDetail?: string;
47
+ }
48
+ /** 사유 마스터에서 필요한 것만. */
49
+ export interface StopReasonFact {
50
+ name: string;
51
+ status: string;
52
+ detailRequired?: boolean;
53
+ }
54
+ /** 이미 적혀 있는 구간 — 겹침을 볼 때만 쓴다. */
55
+ export interface ExistingPeriod {
56
+ startTime: Date | string;
57
+ endTime: Date | string;
58
+ }
59
+ /**
60
+ * 이 구간을 받아도 되나.
61
+ *
62
+ * `now` 를 부르는 쪽이 준다 — 이 함수가 시계를 읽으면 테스트가 시각에 흔들린다.
63
+ */
64
+ export declare function judgePeriod(period: JudgeablePeriod, context: {
65
+ now: Date | string;
66
+ /** 같은 설비의 이미 적힌 구간들. 부르는 쪽이 시각으로 좁혀서 준다. */
67
+ existing?: readonly ExistingPeriod[];
68
+ /**
69
+ * 사유 마스터에서 읽은 것.
70
+ *
71
+ * **`undefined` 와 빈 배열의 뜻이 다르다.** `undefined` 는 「마스터를 아직 채우지 않았다」이고
72
+ * 확인하지 않는다. 빈 배열은 「채웠는데 이 사유가 없다」이고 막는다. 둘을 섞으면 오타로 적은
73
+ * 사유가 오류 보고 없이 통과한다.
74
+ */
75
+ reasons?: readonly StopReasonFact[];
76
+ }): PeriodIssue[];
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * 상태 구간을 받아도 되나 — **순수 판단.**
4
+ *
5
+ * ── 왜 규칙이 필요한가 ──────────────────────────────────────────────────────
6
+ * 구간은 사람이 되돌아가 적는다. 그래서 겹치고, 거꾸로 되고, 미래로 간다. 그대로 받으면 가동률의
7
+ * 분자가 분모보다 커지는 날이 온다. 그때는 이미 잘못된 구간이 수백 개 쌓여 있다.
8
+ *
9
+ * ── 왜 사유와 상태를 맞춰 보나 ──────────────────────────────────────────────
10
+ * ISO 22400 에서 가동률을 깎느냐 마느냐는 **상태**가 정한다. 「점심시간」을 `down` 으로 적으면 가동률이
11
+ * 오류 보고 없이 낮아지고, 「고장」을 `planned-stop` 으로 적으면 오류 보고 없이 높아진다. 둘 다 조용한
12
+ * 거짓이라 나중에 찾을 방법이 없다.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.EQUIPMENT_PERIOD_STATUS = void 0;
16
+ exports.judgePeriod = judgePeriod;
17
+ /** 계약의 `EQUIPMENT_STATUS` 와 같은 어휘. 여기서 새로 만들지 않는다. */
18
+ exports.EQUIPMENT_PERIOD_STATUS = ['idle', 'busy', 'down', 'setup', 'planned-stop'];
19
+ /**
20
+ * 이 구간을 받아도 되나.
21
+ *
22
+ * `now` 를 부르는 쪽이 준다 — 이 함수가 시계를 읽으면 테스트가 시각에 흔들린다.
23
+ */
24
+ function judgePeriod(period, context) {
25
+ const issues = [];
26
+ const start = ms(period.startTime);
27
+ const end = ms(period.endTime);
28
+ if (!(end > start)) {
29
+ issues.push({ problem: 'not-a-period' });
30
+ }
31
+ if (end > ms(context.now)) {
32
+ issues.push({ problem: 'in-the-future' });
33
+ }
34
+ if (!exports.EQUIPMENT_PERIOD_STATUS.includes(period.status)) {
35
+ issues.push({ problem: 'unknown-status', status: period.status });
36
+ }
37
+ for (const other of context.existing ?? []) {
38
+ const otherStart = ms(other.startTime);
39
+ const otherEnd = ms(other.endTime);
40
+ /* 끝과 시작이 맞닿는 것은 겹침이 아니다 — 한 구간이 끝나는 순간 다음이 시작한다. */
41
+ if (start < otherEnd && otherStart < end) {
42
+ issues.push({ problem: 'overlaps', conflictsWith: isoOf(other.startTime) });
43
+ }
44
+ }
45
+ issues.push(...reasonIssues(period, context.reasons));
46
+ return issues;
47
+ }
48
+ function reasonIssues(period, reasons) {
49
+ /*
50
+ * 사유를 적지 않은 것은 흠이 아니다. 정지는 일단 적고 사유는 나중에 정하는 것이 현장의 순서이고,
51
+ * 사유를 요구하면 그 순서를 막게 된다.
52
+ */
53
+ if (!period.reasonCode) {
54
+ return [];
55
+ }
56
+ /*
57
+ * 마스터를 아직 채우지 않은 현장은 확인하지 않는다 — 선언한 것만 걸린다.
58
+ *
59
+ * 빈 배열은 여기서 걸러지지 않는다. 채웠는데 이 사유가 없는 것이고, 아래에서 「모르는 사유」가 된다.
60
+ */
61
+ if (!reasons) {
62
+ return [];
63
+ }
64
+ const found = reasons.find(reason => reason.name === period.reasonCode);
65
+ if (!found) {
66
+ return [{ problem: 'unknown-reason', reasonCode: period.reasonCode }];
67
+ }
68
+ const issues = [];
69
+ if (found.status !== period.status) {
70
+ issues.push({
71
+ problem: 'reason-status-mismatch',
72
+ reasonCode: found.name,
73
+ reasonStatus: found.status,
74
+ status: period.status
75
+ });
76
+ }
77
+ if (found.detailRequired && !period.reasonDetail) {
78
+ issues.push({ problem: 'detail-required', reasonCode: found.name });
79
+ }
80
+ return issues;
81
+ }
82
+ function ms(at) {
83
+ return typeof at === 'string' ? Date.parse(at) : at.getTime();
84
+ }
85
+ function isoOf(at) {
86
+ return typeof at === 'string' ? at : at.toISOString();
87
+ }
88
+ //# sourceMappingURL=state-period-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-period-rules.js","sourceRoot":"","sources":["../../../server/service/equipment/state-period-rules.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 상태 구간을 받아도 되나 — **순수 판단.**\n *\n * ── 왜 규칙이 필요한가 ──────────────────────────────────────────────────────\n * 구간은 사람이 되돌아가 적는다. 그래서 겹치고, 거꾸로 되고, 미래로 간다. 그대로 받으면 가동률의\n * 분자가 분모보다 커지는 날이 온다. 그때는 이미 잘못된 구간이 수백 개 쌓여 있다.\n *\n * ── 왜 사유와 상태를 맞춰 보나 ──────────────────────────────────────────────\n * ISO 22400 에서 가동률을 깎느냐 마느냐는 **상태**가 정한다. 「점심시간」을 `down` 으로 적으면 가동률이\n * 오류 보고 없이 낮아지고, 「고장」을 `planned-stop` 으로 적으면 오류 보고 없이 높아진다. 둘 다 조용한\n * 거짓이라 나중에 찾을 방법이 없다.\n */\n\n/** 계약의 `EQUIPMENT_STATUS` 와 같은 어휘. 여기서 새로 만들지 않는다. */\nexport const EQUIPMENT_PERIOD_STATUS = ['idle', 'busy', 'down', 'setup', 'planned-stop'] as const\n\nexport type EquipmentPeriodStatus = (typeof EQUIPMENT_PERIOD_STATUS)[number]\n\nexport type PeriodProblem =\n /** 끝이 시작보다 앞이거나 같다 — 길이가 없는 구간은 셀 것이 없다. */\n | 'not-a-period'\n /** 아직 오지 않은 시각이다. */\n | 'in-the-future'\n /** 같은 설비의 다른 구간과 겹친다 — 한 설비가 같은 시각에 두 상태일 수 없다. */\n | 'overlaps'\n /** 계약이 모르는 상태다. */\n | 'unknown-status'\n /** 사유가 사유 마스터에 없다. */\n | 'unknown-reason'\n /** 사유가 이 상태의 사유가 아니다 — 가동률이 오류 보고 없이 틀어진다. */\n | 'reason-status-mismatch'\n /** 이 사유는 상세를 함께 적어야 한다. */\n | 'detail-required'\n\nexport interface PeriodIssue {\n problem: PeriodProblem\n /** 겹치는 것이면 어느 구간과 겹치나(시작 시각, ISO). */\n conflictsWith?: string\n /** 사유 문제이면 어느 사유인가. */\n reasonCode?: string\n /** 사유가 속한 상태와 적힌 상태. */\n reasonStatus?: string\n status?: string\n}\n\nexport interface JudgeablePeriod {\n status: string\n startTime: Date | string\n endTime: Date | string\n reasonCode?: string\n reasonDetail?: string\n}\n\n/** 사유 마스터에서 필요한 것만. */\nexport interface StopReasonFact {\n name: string\n status: string\n detailRequired?: boolean\n}\n\n/** 이미 적혀 있는 구간 — 겹침을 볼 때만 쓴다. */\nexport interface ExistingPeriod {\n startTime: Date | string\n endTime: Date | string\n}\n\n/**\n * 이 구간을 받아도 되나.\n *\n * `now` 를 부르는 쪽이 준다 — 이 함수가 시계를 읽으면 테스트가 시각에 흔들린다.\n */\nexport function judgePeriod(\n period: JudgeablePeriod,\n context: {\n now: Date | string\n /** 같은 설비의 이미 적힌 구간들. 부르는 쪽이 시각으로 좁혀서 준다. */\n existing?: readonly ExistingPeriod[]\n /**\n * 사유 마스터에서 읽은 것.\n *\n * **`undefined` 와 빈 배열의 뜻이 다르다.** `undefined` 는 「마스터를 아직 채우지 않았다」이고\n * 확인하지 않는다. 빈 배열은 「채웠는데 이 사유가 없다」이고 막는다. 둘을 섞으면 오타로 적은\n * 사유가 오류 보고 없이 통과한다.\n */\n reasons?: readonly StopReasonFact[]\n }\n): PeriodIssue[] {\n const issues: PeriodIssue[] = []\n\n const start = ms(period.startTime)\n const end = ms(period.endTime)\n\n if (!(end > start)) {\n issues.push({ problem: 'not-a-period' })\n }\n\n if (end > ms(context.now)) {\n issues.push({ problem: 'in-the-future' })\n }\n\n if (!(EQUIPMENT_PERIOD_STATUS as readonly string[]).includes(period.status)) {\n issues.push({ problem: 'unknown-status', status: period.status })\n }\n\n for (const other of context.existing ?? []) {\n const otherStart = ms(other.startTime)\n const otherEnd = ms(other.endTime)\n\n /* 끝과 시작이 맞닿는 것은 겹침이 아니다 — 한 구간이 끝나는 순간 다음이 시작한다. */\n if (start < otherEnd && otherStart < end) {\n issues.push({ problem: 'overlaps', conflictsWith: isoOf(other.startTime) })\n }\n }\n\n issues.push(...reasonIssues(period, context.reasons))\n\n return issues\n}\n\nfunction reasonIssues(period: JudgeablePeriod, reasons: readonly StopReasonFact[] | undefined): PeriodIssue[] {\n /*\n * 사유를 적지 않은 것은 흠이 아니다. 정지는 일단 적고 사유는 나중에 정하는 것이 현장의 순서이고,\n * 사유를 요구하면 그 순서를 막게 된다.\n */\n if (!period.reasonCode) {\n return []\n }\n\n /*\n * 마스터를 아직 채우지 않은 현장은 확인하지 않는다 — 선언한 것만 걸린다.\n *\n * 빈 배열은 여기서 걸러지지 않는다. 채웠는데 이 사유가 없는 것이고, 아래에서 「모르는 사유」가 된다.\n */\n if (!reasons) {\n return []\n }\n\n const found = reasons.find(reason => reason.name === period.reasonCode)\n\n if (!found) {\n return [{ problem: 'unknown-reason', reasonCode: period.reasonCode }]\n }\n\n const issues: PeriodIssue[] = []\n\n if (found.status !== period.status) {\n issues.push({\n problem: 'reason-status-mismatch',\n reasonCode: found.name,\n reasonStatus: found.status,\n status: period.status\n })\n }\n\n if (found.detailRequired && !period.reasonDetail) {\n issues.push({ problem: 'detail-required', reasonCode: found.name })\n }\n\n return issues\n}\n\nfunction ms(at: Date | string): number {\n return typeof at === 'string' ? Date.parse(at) : at.getTime()\n}\n\nfunction isoOf(at: Date | string): string {\n return typeof at === 'string' ? at : at.toISOString()\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import { ListParam } from '@things-factory/shell';
2
+ import { StopReason } from './stop-reason';
3
+ /**
4
+ * 정지 사유 목록.
5
+ *
6
+ * 트윈으로 내보내는 마스터에 넣지 않는다 — 사유는 **MES 가 사실을 적을 때 쓰는 분류**이고, 트윈은
7
+ * 상태 구간에 실려 오는 사유 코드를 그대로 받는다. 목록을 두 곳에 두면 한쪽만 늘어나는 날이 온다.
8
+ */
9
+ export declare class StopReasonQuery {
10
+ stopReasons(params: ListParam, context: ResolverContext): Promise<StopReason[]>;
11
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StopReasonQuery = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const type_graphql_1 = require("type-graphql");
6
+ const shell_1 = require("@things-factory/shell");
7
+ const stop_reason_1 = require("./stop-reason");
8
+ /**
9
+ * 정지 사유 목록.
10
+ *
11
+ * 트윈으로 내보내는 마스터에 넣지 않는다 — 사유는 **MES 가 사실을 적을 때 쓰는 분류**이고, 트윈은
12
+ * 상태 구간에 실려 오는 사유 코드를 그대로 받는다. 목록을 두 곳에 두면 한쪽만 늘어나는 날이 온다.
13
+ */
14
+ let StopReasonQuery = class StopReasonQuery {
15
+ async stopReasons(params, context) {
16
+ const { domain } = context.state;
17
+ const queryBuilder = (0, shell_1.getQueryBuilderFromListParams)({
18
+ repository: (0, shell_1.getRepository)(stop_reason_1.StopReason),
19
+ params,
20
+ domain,
21
+ searchables: ['name', 'description'],
22
+ alias: 'sr'
23
+ });
24
+ return queryBuilder.getMany();
25
+ }
26
+ };
27
+ exports.StopReasonQuery = StopReasonQuery;
28
+ tslib_1.__decorate([
29
+ (0, type_graphql_1.Query)(returns => [stop_reason_1.StopReason], { description: 'To fetch the classified reasons an equipment stops' }),
30
+ tslib_1.__param(0, (0, type_graphql_1.Args)(type => shell_1.ListParam)),
31
+ tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
32
+ tslib_1.__metadata("design:type", Function),
33
+ tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
34
+ tslib_1.__metadata("design:returntype", Promise)
35
+ ], StopReasonQuery.prototype, "stopReasons", null);
36
+ exports.StopReasonQuery = StopReasonQuery = tslib_1.__decorate([
37
+ (0, type_graphql_1.Resolver)(stop_reason_1.StopReason)
38
+ ], StopReasonQuery);
39
+ //# sourceMappingURL=stop-reason-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-reason-query.js","sourceRoot":"","sources":["../../../server/service/equipment/stop-reason-query.ts"],"names":[],"mappings":";;;;AAAA,+CAAyD;AAEzD,iDAA+F;AAE/F,+CAA0C;AAE1C;;;;;GAKG;AAEI,IAAM,eAAe,GAArB,MAAM,eAAe;IAEpB,AAAN,KAAK,CAAC,WAAW,CAA0B,MAAiB,EAAS,OAAwB;QAC3F,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,MAAM,YAAY,GAAG,IAAA,qCAA6B,EAAC;YACjD,UAAU,EAAE,IAAA,qBAAa,EAAC,wBAAU,CAAC;YACrC,MAAM;YACN,MAAM;YACN,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC;YACpC,KAAK,EAAE,IAAI;SACZ,CAAC,CAAA;QAEF,OAAO,YAAY,CAAC,OAAO,EAAE,CAAA;IAC/B,CAAC;CACF,CAAA;AAfY,0CAAe;AAEpB;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,wBAAU,CAAC,EAAE,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC;IACnF,mBAAA,IAAA,mBAAI,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAjB,iBAAS;;kDAY3D;0BAdU,eAAe;IAD3B,IAAA,uBAAQ,EAAC,wBAAU,CAAC;GACR,eAAe,CAe3B","sourcesContent":["import { Args, Ctx, Query, Resolver } from 'type-graphql'\n\nimport { ListParam, getQueryBuilderFromListParams, getRepository } from '@things-factory/shell'\n\nimport { StopReason } from './stop-reason'\n\n/**\n * 정지 사유 목록.\n *\n * 트윈으로 내보내는 마스터에 넣지 않는다 — 사유는 **MES 가 사실을 적을 때 쓰는 분류**이고, 트윈은\n * 상태 구간에 실려 오는 사유 코드를 그대로 받는다. 목록을 두 곳에 두면 한쪽만 늘어나는 날이 온다.\n */\n@Resolver(StopReason)\nexport class StopReasonQuery {\n @Query(returns => [StopReason], { description: 'To fetch the classified reasons an equipment stops' })\n async stopReasons(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<StopReason[]> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n repository: getRepository(StopReason),\n params,\n domain,\n searchables: ['name', 'description'],\n alias: 'sr'\n })\n\n return queryBuilder.getMany()\n }\n}\n"]}