@fmsim/fmsim 2.0.0-beta.9 → 2.0.0-beta.91
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.
- package/_index.html +1 -5
- package/assets/manifest.json +2 -0
- package/assets/playback-test-board.json +8440 -0
- package/dist-client/board-list/board-tile-list.js +196 -12
- package/dist-client/board-list/board-tile-list.js.map +1 -1
- package/dist-client/pages/board-list-page.js +22 -1
- package/dist-client/pages/board-list-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/index.js +1 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/routes.js +35 -0
- package/dist-server/routes.js.map +1 -0
- package/dist-server/service/index.js +5 -2
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/machine-units/machine-units-mock.js +83 -0
- package/dist-server/service/machine-units/machine-units-mock.js.map +1 -0
- package/dist-server/service/playback/index.js +217 -0
- package/dist-server/service/playback/index.js.map +1 -0
- package/dist-server/service/playback/mock-data.js +658 -0
- package/dist-server/service/playback/mock-data.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -23
- package/schema.graphql +202 -2
- package/views/internal-board-full-feature-view.html +0 -1
- package/views/internal-board-service-view.html +0 -1
- package/views/internal-label-command-view.html +0 -1
package/dist-server/index.js
CHANGED
package/dist-server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,oDAAyB","sourcesContent":["
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,oBAAiB;AAEjB,oDAAyB","sourcesContent":["import './routes'\n\nexport * from './service'\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* fmsim 테스트 환경 mock 서버 REST 라우트.
|
|
5
|
+
*
|
|
6
|
+
* 프로덕션(fmsim-restful)은 MCS LV RESTful 서비스가 GET /machineUnits/{type} 를 응답한다.
|
|
7
|
+
* fmsim 은 테스트 환경이므로, 클라이언트(@fmsim/api 의 fetchMachineUnits)가 lv_server_ip_port
|
|
8
|
+
* 미설정 상태에서 same-origin 으로 호출하는 동일 경로를 이 라우트가 mock 으로 응답한다.
|
|
9
|
+
*
|
|
10
|
+
* GET /machineUnits?type={type}[&machine={parentMachine}]
|
|
11
|
+
* - type : 컴포넌트 타입 (query 파라미터)
|
|
12
|
+
* - machine: unit 타입일 때만 — 부모 머신 id (조회 범위 한정)
|
|
13
|
+
* - keyword 는 받지 않는다 (클라이언트가 후보 목록을 받아 로컬 필터)
|
|
14
|
+
*
|
|
15
|
+
* @things-factory 모듈 라우트 등록 규약: shell 이 부팅 시 emit 하는
|
|
16
|
+
* 'bootstrap-module-global-public-route' 이벤트로 globalPublicRouter(koa-router) 를 받아
|
|
17
|
+
* 라우트를 추가한다. (server/index.ts 가 이 모듈을 import 하여 리스너를 등록한다.)
|
|
18
|
+
*/
|
|
19
|
+
const machine_units_mock_1 = require("./service/machine-units/machine-units-mock");
|
|
20
|
+
/** 테스트용 의도적 지연 (0.1 ~ 1.0초) — 클라이언트 비동기 처리 검증용. */
|
|
21
|
+
function mockLatency() {
|
|
22
|
+
const ms = 100 + Math.floor(Math.random() * 900);
|
|
23
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
24
|
+
}
|
|
25
|
+
process.on('bootstrap-module-global-public-route', (_app, globalPublicRouter) => {
|
|
26
|
+
globalPublicRouter.get('/machineUnits', async (ctx) => {
|
|
27
|
+
const type = typeof ctx.query.type === 'string' ? ctx.query.type : '';
|
|
28
|
+
const machine = typeof ctx.query.machine === 'string' ? ctx.query.machine : undefined;
|
|
29
|
+
// 테스트 환경 mock — 실제 서버 지연을 모사해 클라이언트 비동기 흐름(로딩 표시 등) 확인.
|
|
30
|
+
await mockLatency();
|
|
31
|
+
ctx.type = 'application/json';
|
|
32
|
+
ctx.body = (0, machine_units_mock_1.searchMockMachineUnits)(type, machine ? { machine } : {});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../server/routes.ts"],"names":[],"mappings":";;AAAA;;;;;;;;;;;;;;;GAeG;AACH,mFAAmF;AAEnF,mDAAmD;AACnD,SAAS,WAAW;IAClB,MAAM,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAA;IAChD,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;AACxD,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,sCAA6C,EAAE,CAAC,IAAS,EAAE,kBAAuB,EAAE,EAAE;IAC/F,kBAAkB,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,EAAE,GAAQ,EAAE,EAAE;QACzD,MAAM,IAAI,GAAW,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;QAC7E,MAAM,OAAO,GAAuB,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;QAEzG,wDAAwD;QACxD,MAAM,WAAW,EAAE,CAAA;QAEnB,GAAG,CAAC,IAAI,GAAG,kBAAkB,CAAA;QAC7B,GAAG,CAAC,IAAI,GAAG,IAAA,2CAAsB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IACrE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["/**\n * fmsim 테스트 환경 mock 서버 REST 라우트.\n *\n * 프로덕션(fmsim-restful)은 MCS LV RESTful 서비스가 GET /machineUnits/{type} 를 응답한다.\n * fmsim 은 테스트 환경이므로, 클라이언트(@fmsim/api 의 fetchMachineUnits)가 lv_server_ip_port\n * 미설정 상태에서 same-origin 으로 호출하는 동일 경로를 이 라우트가 mock 으로 응답한다.\n *\n * GET /machineUnits?type={type}[&machine={parentMachine}]\n * - type : 컴포넌트 타입 (query 파라미터)\n * - machine: unit 타입일 때만 — 부모 머신 id (조회 범위 한정)\n * - keyword 는 받지 않는다 (클라이언트가 후보 목록을 받아 로컬 필터)\n *\n * @things-factory 모듈 라우트 등록 규약: shell 이 부팅 시 emit 하는\n * 'bootstrap-module-global-public-route' 이벤트로 globalPublicRouter(koa-router) 를 받아\n * 라우트를 추가한다. (server/index.ts 가 이 모듈을 import 하여 리스너를 등록한다.)\n */\nimport { searchMockMachineUnits } from './service/machine-units/machine-units-mock'\n\n/** 테스트용 의도적 지연 (0.1 ~ 1.0초) — 클라이언트 비동기 처리 검증용. */\nfunction mockLatency(): Promise<void> {\n const ms = 100 + Math.floor(Math.random() * 900)\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nprocess.on('bootstrap-module-global-public-route' as any, (_app: any, globalPublicRouter: any) => {\n globalPublicRouter.get('/machineUnits', async (ctx: any) => {\n const type: string = typeof ctx.query.type === 'string' ? ctx.query.type : ''\n const machine: string | undefined = typeof ctx.query.machine === 'string' ? ctx.query.machine : undefined\n\n // 테스트 환경 mock — 실제 서버 지연을 모사해 클라이언트 비동기 흐름(로딩 표시 등) 확인.\n await mockLatency()\n\n ctx.type = 'application/json'\n ctx.body = searchMockMachineUnits(type, machine ? { machine } : {})\n })\n})\n"]}
|
|
@@ -4,14 +4,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.schema = exports.entities = void 0;
|
|
5
5
|
/* IMPORT ENTITIES AND RESOLVERS */
|
|
6
6
|
const board_favorite_1 = require("./board-favorite");
|
|
7
|
+
const playback_1 = require("./playback");
|
|
7
8
|
exports.entities = [
|
|
8
9
|
/* ENTITIES */
|
|
9
|
-
...board_favorite_1.entities
|
|
10
|
+
...board_favorite_1.entities,
|
|
11
|
+
...playback_1.entities
|
|
10
12
|
];
|
|
11
13
|
exports.schema = {
|
|
12
14
|
resolverClasses: [
|
|
13
15
|
/* RESOLVER CLASSES */
|
|
14
|
-
...board_favorite_1.resolvers
|
|
16
|
+
...board_favorite_1.resolvers,
|
|
17
|
+
...playback_1.resolvers
|
|
15
18
|
]
|
|
16
19
|
};
|
|
17
20
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";AAAA,yBAAyB;;;AAEzB,mCAAmC;AACnC,qDAAyG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";AAAA,yBAAyB;;;AAEzB,mCAAmC;AACnC,qDAAyG;AACzG,yCAAyF;AAE5E,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,yBAAqB;IACxB,GAAG,mBAAgB;CACpB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,0BAAsB;QACzB,GAAG,oBAAiB;KACrB;CACF,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as BoardFavoriteEntities, resolvers as BoardFavoriteResolvers } from './board-favorite'\nimport { entities as PlaybackEntities, resolvers as PlaybackResolvers } from './playback'\n\nexport const entities = [\n /* ENTITIES */\n ...BoardFavoriteEntities,\n ...PlaybackEntities\n]\n\nexport const schema = {\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...BoardFavoriteResolvers,\n ...PlaybackResolvers\n ]\n}\n"]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* 머신/유닛 ID 검색 — 서버측 가상(mock) 데이터.
|
|
4
|
+
*
|
|
5
|
+
* 프로덕션(fmsim-restful)은 MCS LV RESTful 서비스가 /machineUnits/{type} 를 응답한다.
|
|
6
|
+
* fmsim 은 테스트 환경이므로 이 모듈이 같은 형태의 가상 응답을 만들어 mock 라우트가 응답한다.
|
|
7
|
+
*
|
|
8
|
+
* 엔티티는 machine 과 unit 으로 나뉜다 (키 = 실제 모델 타입):
|
|
9
|
+
* - machine: 독립 엔티티 (STOCKER, Conveyor, Equipment, Buffer, AGV, OHT, Shuttle, …)
|
|
10
|
+
* - unit: 어떤 machine 에 소속되는 엔티티 (Port, Shelf, Crane).
|
|
11
|
+
*
|
|
12
|
+
* 결과는 **1차원 id 리스트(string[])**:
|
|
13
|
+
* - machine 조회 → 머신 id 리스트.
|
|
14
|
+
* - unit 조회 → unit id 리스트 (머신별 그룹핑 없음). unit id 는 부모 머신을 접두로
|
|
15
|
+
* 포함한다(예: STK01-P03). opts.machine 지정 시 그 머신 소속으로 좁힌다.
|
|
16
|
+
*
|
|
17
|
+
* 외부 의존 없는 순수 함수.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.MACHINE_UNIT_SEARCH_LIMIT = void 0;
|
|
21
|
+
exports.searchMockMachineUnits = searchMockMachineUnits;
|
|
22
|
+
function seq(prefix, count, pad = 2) {
|
|
23
|
+
return Array.from({ length: count }, (_, i) => `${prefix}${String(i + 1).padStart(pad, '0')}`);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* machine 타입 → 머신 id 후보 selector.
|
|
27
|
+
* 키는 **실제 모델 타입(@sceneComponent 등록명)** — 'STOCKER'(대문자) 등 등록 그대로.
|
|
28
|
+
*/
|
|
29
|
+
const MACHINE_TYPES = {
|
|
30
|
+
STOCKER: () => seq('STK', 10),
|
|
31
|
+
Equipment: () => seq('EQP', 20),
|
|
32
|
+
Buffer: () => seq('BUF', 10),
|
|
33
|
+
Conveyor: () => seq('CNV', 30),
|
|
34
|
+
ConveyorJoin: () => seq('CNVJ', 10),
|
|
35
|
+
CraneRail: () => seq('CRAIL', 6),
|
|
36
|
+
OHTLine: () => seq('OHTLINE', 3),
|
|
37
|
+
AGVLine: () => seq('AGVLINE', 6),
|
|
38
|
+
AGV: () => seq('AGV', 20),
|
|
39
|
+
OHT: () => seq('OHT', 10),
|
|
40
|
+
Shuttle: () => seq('SHTL', 8)
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* unit 타입 → 부모 machine 에 소속. 키는 실제 모델 타입. id = `{machine}-{prefix}{nn}`.
|
|
44
|
+
*/
|
|
45
|
+
const UNIT_TYPES = {
|
|
46
|
+
Port: { prefix: 'P', count: 20 },
|
|
47
|
+
Shelf: { prefix: 'SH', count: 30 },
|
|
48
|
+
Crane: { prefix: 'CR', count: 2 }
|
|
49
|
+
};
|
|
50
|
+
/** machine 미지정 시 unit 을 펼칠 표본 부모 머신 (Stocker). */
|
|
51
|
+
const SAMPLE_UNIT_PARENTS = seq('STK', 10);
|
|
52
|
+
/** 응답 ID 총 개수 상한. */
|
|
53
|
+
exports.MACHINE_UNIT_SEARCH_LIMIT = 50;
|
|
54
|
+
/**
|
|
55
|
+
* 컴포넌트 타입으로 후보 id 리스트를 생성 (가상). **1차원 string[]** 반환.
|
|
56
|
+
*
|
|
57
|
+
* @param type 실제 모델 타입 (예: 'STOCKER', 'AGV', 'Port'). 정확 매칭.
|
|
58
|
+
* @param opts machine(부모 머신 id, unit 조회 시) / limit.
|
|
59
|
+
* @returns string[] (총 id 개수 limit 까지). 매핑 없는 타입은 빈 배열.
|
|
60
|
+
*/
|
|
61
|
+
function searchMockMachineUnits(type, opts = {}) {
|
|
62
|
+
const t = String(type || '');
|
|
63
|
+
const { machine, limit = exports.MACHINE_UNIT_SEARCH_LIMIT } = opts;
|
|
64
|
+
const cap = Math.max(0, limit);
|
|
65
|
+
const unitSpec = UNIT_TYPES[t];
|
|
66
|
+
if (unitSpec) {
|
|
67
|
+
// unit 조회 — 부모 머신 접두를 포함한 unit id 평면 리스트
|
|
68
|
+
const parents = machine ? [machine] : SAMPLE_UNIT_PARENTS;
|
|
69
|
+
const out = [];
|
|
70
|
+
for (const parent of parents) {
|
|
71
|
+
if (out.length >= cap)
|
|
72
|
+
break;
|
|
73
|
+
out.push(...seq(`${parent}-${unitSpec.prefix}`, unitSpec.count).slice(0, cap - out.length));
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
// machine 조회 — 머신 id 평면 리스트
|
|
78
|
+
const selector = MACHINE_TYPES[t];
|
|
79
|
+
if (!selector)
|
|
80
|
+
return [];
|
|
81
|
+
return selector().slice(0, cap);
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=machine-units-mock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine-units-mock.js","sourceRoot":"","sources":["../../../server/service/machine-units/machine-units-mock.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;;AAqDH,wDAqBC;AAjED,SAAS,GAAG,CAAC,MAAc,EAAE,KAAa,EAAE,MAAc,CAAC;IACzD,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;AAChG,CAAC;AAED;;;GAGG;AACH,MAAM,aAAa,GAAmC;IACpD,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC;IAC7B,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC;IAC/B,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC;IAC5B,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC;IAC9B,YAAY,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;IACnC,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAChC,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;IAChC,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;IAChC,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC;IACzB,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC;IACzB,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;CAC9B,CAAA;AAED;;GAEG;AACH,MAAM,UAAU,GAAsD;IACpE,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;IAChC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;IAClC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE;CAClC,CAAA;AAED,kDAAkD;AAClD,MAAM,mBAAmB,GAAG,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;AAE1C,qBAAqB;AACR,QAAA,yBAAyB,GAAG,EAAE,CAAA;AAE3C;;;;;;GAMG;AACH,SAAgB,sBAAsB,CAAC,IAAY,EAAE,OAAiC,EAAE;IACtF,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;IAC5B,MAAM,EAAE,OAAO,EAAE,KAAK,GAAG,iCAAyB,EAAE,GAAG,IAAI,CAAA;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IAE9B,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,CAAA;IAC9B,IAAI,QAAQ,EAAE,CAAC;QACb,yCAAyC;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAA;QACzD,MAAM,GAAG,GAAa,EAAE,CAAA;QACxB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG;gBAAE,MAAK;YAC5B,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,GAAG,MAAM,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;QAC7F,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,4BAA4B;IAC5B,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;IACjC,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAA;IACxB,OAAO,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AACjC,CAAC","sourcesContent":["/*\n * 머신/유닛 ID 검색 — 서버측 가상(mock) 데이터.\n *\n * 프로덕션(fmsim-restful)은 MCS LV RESTful 서비스가 /machineUnits/{type} 를 응답한다.\n * fmsim 은 테스트 환경이므로 이 모듈이 같은 형태의 가상 응답을 만들어 mock 라우트가 응답한다.\n *\n * 엔티티는 machine 과 unit 으로 나뉜다 (키 = 실제 모델 타입):\n * - machine: 독립 엔티티 (STOCKER, Conveyor, Equipment, Buffer, AGV, OHT, Shuttle, …)\n * - unit: 어떤 machine 에 소속되는 엔티티 (Port, Shelf, Crane).\n *\n * 결과는 **1차원 id 리스트(string[])**:\n * - machine 조회 → 머신 id 리스트.\n * - unit 조회 → unit id 리스트 (머신별 그룹핑 없음). unit id 는 부모 머신을 접두로\n * 포함한다(예: STK01-P03). opts.machine 지정 시 그 머신 소속으로 좁힌다.\n *\n * 외부 의존 없는 순수 함수.\n */\n\nexport interface MachineUnitSearchOptions {\n /** 부모 machine id — unit 조회를 그 machine 소속으로 좁힌다 (선택). */\n machine?: string\n /** 결과 id 총 개수 상한 (기본 MACHINE_UNIT_SEARCH_LIMIT). */\n limit?: number\n}\n\nfunction seq(prefix: string, count: number, pad: number = 2): string[] {\n return Array.from({ length: count }, (_, i) => `${prefix}${String(i + 1).padStart(pad, '0')}`)\n}\n\n/**\n * machine 타입 → 머신 id 후보 selector.\n * 키는 **실제 모델 타입(@sceneComponent 등록명)** — 'STOCKER'(대문자) 등 등록 그대로.\n */\nconst MACHINE_TYPES: Record<string, () => string[]> = {\n STOCKER: () => seq('STK', 10),\n Equipment: () => seq('EQP', 20),\n Buffer: () => seq('BUF', 10),\n Conveyor: () => seq('CNV', 30),\n ConveyorJoin: () => seq('CNVJ', 10),\n CraneRail: () => seq('CRAIL', 6),\n OHTLine: () => seq('OHTLINE', 3),\n AGVLine: () => seq('AGVLINE', 6),\n AGV: () => seq('AGV', 20),\n OHT: () => seq('OHT', 10),\n Shuttle: () => seq('SHTL', 8)\n}\n\n/**\n * unit 타입 → 부모 machine 에 소속. 키는 실제 모델 타입. id = `{machine}-{prefix}{nn}`.\n */\nconst UNIT_TYPES: Record<string, { prefix: string; count: number }> = {\n Port: { prefix: 'P', count: 20 },\n Shelf: { prefix: 'SH', count: 30 },\n Crane: { prefix: 'CR', count: 2 }\n}\n\n/** machine 미지정 시 unit 을 펼칠 표본 부모 머신 (Stocker). */\nconst SAMPLE_UNIT_PARENTS = seq('STK', 10)\n\n/** 응답 ID 총 개수 상한. */\nexport const MACHINE_UNIT_SEARCH_LIMIT = 50\n\n/**\n * 컴포넌트 타입으로 후보 id 리스트를 생성 (가상). **1차원 string[]** 반환.\n *\n * @param type 실제 모델 타입 (예: 'STOCKER', 'AGV', 'Port'). 정확 매칭.\n * @param opts machine(부모 머신 id, unit 조회 시) / limit.\n * @returns string[] (총 id 개수 limit 까지). 매핑 없는 타입은 빈 배열.\n */\nexport function searchMockMachineUnits(type: string, opts: MachineUnitSearchOptions = {}): string[] {\n const t = String(type || '')\n const { machine, limit = MACHINE_UNIT_SEARCH_LIMIT } = opts\n const cap = Math.max(0, limit)\n\n const unitSpec = UNIT_TYPES[t]\n if (unitSpec) {\n // unit 조회 — 부모 머신 접두를 포함한 unit id 평면 리스트\n const parents = machine ? [machine] : SAMPLE_UNIT_PARENTS\n const out: string[] = []\n for (const parent of parents) {\n if (out.length >= cap) break\n out.push(...seq(`${parent}-${unitSpec.prefix}`, unitSpec.count).slice(0, cap - out.length))\n }\n return out\n }\n\n // machine 조회 — 머신 id 평면 리스트\n const selector = MACHINE_TYPES[t]\n if (!selector) return []\n return selector().slice(0, cap)\n}\n"]}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolvers = exports.entities = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const mock_data_1 = require("./mock-data");
|
|
7
|
+
let PlaybackRecord = class PlaybackRecord {
|
|
8
|
+
};
|
|
9
|
+
tslib_1.__decorate([
|
|
10
|
+
(0, type_graphql_1.Field)(),
|
|
11
|
+
tslib_1.__metadata("design:type", String)
|
|
12
|
+
], PlaybackRecord.prototype, "type", void 0);
|
|
13
|
+
tslib_1.__decorate([
|
|
14
|
+
(0, type_graphql_1.Field)(),
|
|
15
|
+
tslib_1.__metadata("design:type", String)
|
|
16
|
+
], PlaybackRecord.prototype, "time", void 0);
|
|
17
|
+
tslib_1.__decorate([
|
|
18
|
+
(0, type_graphql_1.Field)(),
|
|
19
|
+
tslib_1.__metadata("design:type", String)
|
|
20
|
+
], PlaybackRecord.prototype, "data", void 0);
|
|
21
|
+
PlaybackRecord = tslib_1.__decorate([
|
|
22
|
+
(0, type_graphql_1.ObjectType)()
|
|
23
|
+
], PlaybackRecord);
|
|
24
|
+
let PlaybackScenarioType = class PlaybackScenarioType {
|
|
25
|
+
};
|
|
26
|
+
tslib_1.__decorate([
|
|
27
|
+
(0, type_graphql_1.Field)(),
|
|
28
|
+
tslib_1.__metadata("design:type", String)
|
|
29
|
+
], PlaybackScenarioType.prototype, "scenarioName", void 0);
|
|
30
|
+
tslib_1.__decorate([
|
|
31
|
+
(0, type_graphql_1.Field)(() => [PlaybackRecord]),
|
|
32
|
+
tslib_1.__metadata("design:type", Array)
|
|
33
|
+
], PlaybackScenarioType.prototype, "snapshots", void 0);
|
|
34
|
+
PlaybackScenarioType = tslib_1.__decorate([
|
|
35
|
+
(0, type_graphql_1.ObjectType)()
|
|
36
|
+
], PlaybackScenarioType);
|
|
37
|
+
let PlaybackResult = class PlaybackResult {
|
|
38
|
+
};
|
|
39
|
+
tslib_1.__decorate([
|
|
40
|
+
(0, type_graphql_1.Field)(() => [PlaybackScenarioType]),
|
|
41
|
+
tslib_1.__metadata("design:type", Array)
|
|
42
|
+
], PlaybackResult.prototype, "scenarios", void 0);
|
|
43
|
+
PlaybackResult = tslib_1.__decorate([
|
|
44
|
+
(0, type_graphql_1.ObjectType)()
|
|
45
|
+
], PlaybackResult);
|
|
46
|
+
let PlaybackResponse = class PlaybackResponse {
|
|
47
|
+
};
|
|
48
|
+
tslib_1.__decorate([
|
|
49
|
+
(0, type_graphql_1.Field)(),
|
|
50
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
51
|
+
], PlaybackResponse.prototype, "status", void 0);
|
|
52
|
+
tslib_1.__decorate([
|
|
53
|
+
(0, type_graphql_1.Field)({ nullable: true }),
|
|
54
|
+
tslib_1.__metadata("design:type", String)
|
|
55
|
+
], PlaybackResponse.prototype, "errorMessage", void 0);
|
|
56
|
+
tslib_1.__decorate([
|
|
57
|
+
(0, type_graphql_1.Field)(() => PlaybackResult, { nullable: true }),
|
|
58
|
+
tslib_1.__metadata("design:type", PlaybackResult)
|
|
59
|
+
], PlaybackResponse.prototype, "playback", void 0);
|
|
60
|
+
PlaybackResponse = tslib_1.__decorate([
|
|
61
|
+
(0, type_graphql_1.ObjectType)()
|
|
62
|
+
], PlaybackResponse);
|
|
63
|
+
let MockVehicles = class MockVehicles {
|
|
64
|
+
};
|
|
65
|
+
tslib_1.__decorate([
|
|
66
|
+
(0, type_graphql_1.Field)(() => [String]),
|
|
67
|
+
tslib_1.__metadata("design:type", Array)
|
|
68
|
+
], MockVehicles.prototype, "agvs", void 0);
|
|
69
|
+
tslib_1.__decorate([
|
|
70
|
+
(0, type_graphql_1.Field)(() => [String]),
|
|
71
|
+
tslib_1.__metadata("design:type", Array)
|
|
72
|
+
], MockVehicles.prototype, "ohts", void 0);
|
|
73
|
+
MockVehicles = tslib_1.__decorate([
|
|
74
|
+
(0, type_graphql_1.ObjectType)()
|
|
75
|
+
], MockVehicles);
|
|
76
|
+
let MockEntities = class MockEntities {
|
|
77
|
+
};
|
|
78
|
+
tslib_1.__decorate([
|
|
79
|
+
(0, type_graphql_1.Field)(() => MockVehicles),
|
|
80
|
+
tslib_1.__metadata("design:type", MockVehicles)
|
|
81
|
+
], MockEntities.prototype, "vehicles", void 0);
|
|
82
|
+
tslib_1.__decorate([
|
|
83
|
+
(0, type_graphql_1.Field)(() => [String]),
|
|
84
|
+
tslib_1.__metadata("design:type", Array)
|
|
85
|
+
], MockEntities.prototype, "ports", void 0);
|
|
86
|
+
tslib_1.__decorate([
|
|
87
|
+
(0, type_graphql_1.Field)(() => [String]),
|
|
88
|
+
tslib_1.__metadata("design:type", Array)
|
|
89
|
+
], MockEntities.prototype, "zones", void 0);
|
|
90
|
+
tslib_1.__decorate([
|
|
91
|
+
(0, type_graphql_1.Field)(() => [String]),
|
|
92
|
+
tslib_1.__metadata("design:type", Array)
|
|
93
|
+
], MockEntities.prototype, "stockers", void 0);
|
|
94
|
+
MockEntities = tslib_1.__decorate([
|
|
95
|
+
(0, type_graphql_1.ObjectType)()
|
|
96
|
+
], MockEntities);
|
|
97
|
+
let CarrierLocation = class CarrierLocation {
|
|
98
|
+
};
|
|
99
|
+
tslib_1.__decorate([
|
|
100
|
+
(0, type_graphql_1.Field)(),
|
|
101
|
+
tslib_1.__metadata("design:type", String)
|
|
102
|
+
], CarrierLocation.prototype, "time", void 0);
|
|
103
|
+
tslib_1.__decorate([
|
|
104
|
+
(0, type_graphql_1.Field)(),
|
|
105
|
+
tslib_1.__metadata("design:type", String)
|
|
106
|
+
], CarrierLocation.prototype, "machineName", void 0);
|
|
107
|
+
tslib_1.__decorate([
|
|
108
|
+
(0, type_graphql_1.Field)(),
|
|
109
|
+
tslib_1.__metadata("design:type", String)
|
|
110
|
+
], CarrierLocation.prototype, "unitName", void 0);
|
|
111
|
+
CarrierLocation = tslib_1.__decorate([
|
|
112
|
+
(0, type_graphql_1.ObjectType)()
|
|
113
|
+
], CarrierLocation);
|
|
114
|
+
let CarrierLocationHistoryResponse = class CarrierLocationHistoryResponse {
|
|
115
|
+
};
|
|
116
|
+
tslib_1.__decorate([
|
|
117
|
+
(0, type_graphql_1.Field)({ nullable: true }),
|
|
118
|
+
tslib_1.__metadata("design:type", String)
|
|
119
|
+
], CarrierLocationHistoryResponse.prototype, "errorMessage", void 0);
|
|
120
|
+
tslib_1.__decorate([
|
|
121
|
+
(0, type_graphql_1.Field)(() => [CarrierLocation]),
|
|
122
|
+
tslib_1.__metadata("design:type", Array)
|
|
123
|
+
], CarrierLocationHistoryResponse.prototype, "carrierLocHistory", void 0);
|
|
124
|
+
CarrierLocationHistoryResponse = tslib_1.__decorate([
|
|
125
|
+
(0, type_graphql_1.ObjectType)()
|
|
126
|
+
], CarrierLocationHistoryResponse);
|
|
127
|
+
let PlaybackResolver = class PlaybackResolver {
|
|
128
|
+
async playback(startTime, endTime, count, includeSnapshot) {
|
|
129
|
+
try {
|
|
130
|
+
// endTime 또는 count 중 하나로 조회 범위 지정.
|
|
131
|
+
// 둘 다 없으면 기본 1시간 (endTime 기반).
|
|
132
|
+
let opts;
|
|
133
|
+
if (endTime) {
|
|
134
|
+
opts = { endTime };
|
|
135
|
+
}
|
|
136
|
+
else if (count && count > 0) {
|
|
137
|
+
opts = { count };
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const start = new Date(startTime.replace(' ', 'T'));
|
|
141
|
+
const defaultEnd = new Date(start.getTime() + 3600000)
|
|
142
|
+
.toISOString()
|
|
143
|
+
.replace('T', ' ')
|
|
144
|
+
.replace(/\.\d+Z$/, '');
|
|
145
|
+
opts = { endTime: defaultEnd };
|
|
146
|
+
}
|
|
147
|
+
const scenarios = (0, mock_data_1.generatePlaybackData)(startTime, opts);
|
|
148
|
+
// includeSnapshot=false이면 snapshot 프레임 제거
|
|
149
|
+
if (!includeSnapshot) {
|
|
150
|
+
for (const s of scenarios) {
|
|
151
|
+
s.snapshots = s.snapshots.filter(f => f.type !== 'snapshot');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// data 배열을 JSON string으로 직렬화
|
|
155
|
+
const serialized = scenarios.map(s => (Object.assign(Object.assign({}, s), { snapshots: s.snapshots.map(f => ({
|
|
156
|
+
type: f.type,
|
|
157
|
+
time: f.time,
|
|
158
|
+
data: JSON.stringify(f.data)
|
|
159
|
+
})) })));
|
|
160
|
+
return {
|
|
161
|
+
status: true,
|
|
162
|
+
playback: { scenarios: serialized }
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
return {
|
|
167
|
+
status: false,
|
|
168
|
+
errorMessage: e.message || String(e)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async playbackEntities() {
|
|
173
|
+
return mock_data_1.MOCK_ENTITIES;
|
|
174
|
+
}
|
|
175
|
+
async carrierLocationHistory(startTime, endTime, carrierName) {
|
|
176
|
+
try {
|
|
177
|
+
const carrierLocHistory = (0, mock_data_1.generateCarrierLocationHistory)(startTime, endTime, carrierName);
|
|
178
|
+
return { errorMessage: null, carrierLocHistory };
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
return { errorMessage: (e === null || e === void 0 ? void 0 : e.message) || String(e), carrierLocHistory: [] };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
tslib_1.__decorate([
|
|
186
|
+
(0, type_graphql_1.Query)(() => PlaybackResponse, { description: 'Playback 데이터 조회 (목업)' }),
|
|
187
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('startTime')),
|
|
188
|
+
tslib_1.__param(1, (0, type_graphql_1.Arg)('endTime', { nullable: true })),
|
|
189
|
+
tslib_1.__param(2, (0, type_graphql_1.Arg)('count', () => type_graphql_1.Int, { nullable: true })),
|
|
190
|
+
tslib_1.__param(3, (0, type_graphql_1.Arg)('includeSnapshot', { nullable: true, defaultValue: true })),
|
|
191
|
+
tslib_1.__metadata("design:type", Function),
|
|
192
|
+
tslib_1.__metadata("design:paramtypes", [String, String, Number, Boolean]),
|
|
193
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
194
|
+
], PlaybackResolver.prototype, "playback", null);
|
|
195
|
+
tslib_1.__decorate([
|
|
196
|
+
(0, type_graphql_1.Query)(() => MockEntities, { description: '목업 엔티티 목록 (보드 작화용)' }),
|
|
197
|
+
tslib_1.__metadata("design:type", Function),
|
|
198
|
+
tslib_1.__metadata("design:paramtypes", []),
|
|
199
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
200
|
+
], PlaybackResolver.prototype, "playbackEntities", null);
|
|
201
|
+
tslib_1.__decorate([
|
|
202
|
+
(0, type_graphql_1.Query)(() => CarrierLocationHistoryResponse, {
|
|
203
|
+
description: 'Carrier 위치 이력 조회 (목업). 영구추적 fallback 용.'
|
|
204
|
+
}),
|
|
205
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('startTime')),
|
|
206
|
+
tslib_1.__param(1, (0, type_graphql_1.Arg)('endTime')),
|
|
207
|
+
tslib_1.__param(2, (0, type_graphql_1.Arg)('carrierName')),
|
|
208
|
+
tslib_1.__metadata("design:type", Function),
|
|
209
|
+
tslib_1.__metadata("design:paramtypes", [String, String, String]),
|
|
210
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
211
|
+
], PlaybackResolver.prototype, "carrierLocationHistory", null);
|
|
212
|
+
PlaybackResolver = tslib_1.__decorate([
|
|
213
|
+
(0, type_graphql_1.Resolver)()
|
|
214
|
+
], PlaybackResolver);
|
|
215
|
+
exports.entities = [];
|
|
216
|
+
exports.resolvers = [PlaybackResolver];
|
|
217
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/playback/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Query, Arg, ObjectType, Field, Int } from 'type-graphql'\nimport { generatePlaybackData, generateCarrierLocationHistory, MOCK_ENTITIES } from './mock-data'\n\n@ObjectType()\nclass PlaybackRecord {\n @Field() type!: string\n @Field() time!: string\n @Field() data!: string // JSON string\n}\n\n@ObjectType()\nclass PlaybackScenarioType {\n @Field() scenarioName!: string\n @Field(() => [PlaybackRecord]) snapshots!: PlaybackRecord[]\n}\n\n@ObjectType()\nclass PlaybackResult {\n @Field(() => [PlaybackScenarioType]) scenarios!: PlaybackScenarioType[]\n}\n\n@ObjectType()\nclass PlaybackResponse {\n @Field() status!: boolean\n @Field({ nullable: true }) errorMessage?: string\n @Field(() => PlaybackResult, { nullable: true }) playback?: PlaybackResult\n}\n\n@ObjectType()\nclass MockVehicles {\n @Field(() => [String]) agvs!: string[]\n @Field(() => [String]) ohts!: string[]\n}\n\n@ObjectType()\nclass MockEntities {\n @Field(() => MockVehicles) vehicles!: MockVehicles\n @Field(() => [String]) ports!: string[]\n @Field(() => [String]) zones!: string[]\n @Field(() => [String]) stockers!: string[]\n}\n\n@ObjectType()\nclass CarrierLocation {\n @Field() time!: string\n @Field() machineName!: string\n @Field() unitName!: string\n}\n\n@ObjectType()\nclass CarrierLocationHistoryResponse {\n @Field({ nullable: true }) errorMessage?: string | null\n @Field(() => [CarrierLocation]) carrierLocHistory!: CarrierLocation[]\n}\n\n@Resolver()\nclass PlaybackResolver {\n @Query(() => PlaybackResponse, { description: 'Playback 데이터 조회 (목업)' })\n async playback(\n @Arg('startTime') startTime: string,\n @Arg('endTime', { nullable: true }) endTime: string,\n @Arg('count', () => Int, { nullable: true }) count: number,\n @Arg('includeSnapshot', { nullable: true, defaultValue: true }) includeSnapshot: boolean\n ): Promise<PlaybackResponse> {\n try {\n // endTime 또는 count 중 하나로 조회 범위 지정.\n // 둘 다 없으면 기본 1시간 (endTime 기반).\n let opts: { endTime?: string; count?: number }\n if (endTime) {\n opts = { endTime }\n } else if (count && count > 0) {\n opts = { count }\n } else {\n const start = new Date(startTime.replace(' ', 'T'))\n const defaultEnd = new Date(start.getTime() + 3600000)\n .toISOString()\n .replace('T', ' ')\n .replace(/\\.\\d+Z$/, '')\n opts = { endTime: defaultEnd }\n }\n\n const scenarios = generatePlaybackData(startTime, opts)\n\n // includeSnapshot=false이면 snapshot 프레임 제거\n if (!includeSnapshot) {\n for (const s of scenarios) {\n s.snapshots = s.snapshots.filter(f => f.type !== 'snapshot')\n }\n }\n\n // data 배열을 JSON string으로 직렬화\n const serialized = scenarios.map(s => ({\n ...s,\n snapshots: s.snapshots.map(f => ({\n type: f.type,\n time: f.time,\n data: JSON.stringify(f.data)\n }))\n }))\n\n return {\n status: true,\n playback: { scenarios: serialized }\n }\n } catch (e: any) {\n return {\n status: false,\n errorMessage: e.message || String(e)\n }\n }\n }\n\n @Query(() => MockEntities, { description: '목업 엔티티 목록 (보드 작화용)' })\n async playbackEntities(): Promise<MockEntities> {\n return MOCK_ENTITIES\n }\n\n @Query(() => CarrierLocationHistoryResponse, {\n description: 'Carrier 위치 이력 조회 (목업). 영구추적 fallback 용.'\n })\n async carrierLocationHistory(\n @Arg('startTime') startTime: string,\n @Arg('endTime') endTime: string,\n @Arg('carrierName') carrierName: string\n ): Promise<CarrierLocationHistoryResponse> {\n try {\n const carrierLocHistory = generateCarrierLocationHistory(startTime, endTime, carrierName)\n return { errorMessage: null, carrierLocHistory }\n } catch (e: any) {\n return { errorMessage: e?.message || String(e), carrierLocHistory: [] }\n }\n }\n}\n\nexport const entities = []\nexport const resolvers = [PlaybackResolver]\n"]}
|