@operato/twin-kernel 0.7.33 → 0.7.37
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/dist/face2-adapter.d.ts +42 -3
- package/dist/face2-adapter.js +101 -21
- package/dist-cjs/index.cjs +75 -18
- package/package.json +1 -1
package/dist/face2-adapter.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { CanonicalEnvelope } from './contract.ts';
|
|
|
2
2
|
import type { EpcisEvent } from './epcis.ts';
|
|
3
3
|
/** 매핑 값: 리터럴, 또는 소스 필드 참조("$.field.path"). */
|
|
4
4
|
export type MapValue = string;
|
|
5
|
-
/** ObjectEvent 매핑 스펙(선언적).
|
|
5
|
+
/** ObjectEvent 매핑 스펙(선언적). */
|
|
6
6
|
export interface ObjectEventMapping {
|
|
7
7
|
type: 'ObjectEvent';
|
|
8
8
|
action: MapValue;
|
|
@@ -12,7 +12,41 @@ export interface ObjectEventMapping {
|
|
|
12
12
|
readPoint?: MapValue;
|
|
13
13
|
bizLocation?: MapValue;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* AggregationEvent 매핑 스펙 — **담김의 사실**(무엇이 무엇에 실렸나).
|
|
17
|
+
*
|
|
18
|
+
* ── 왜 뒤늦게 생겼나 (2026-08-19) ───────────────────────────────────────────
|
|
19
|
+
* 이 파일은 오래 *"Agg/Txn 매핑은 확장 지점"* 이라고만 적어 두었고, 그래서 원본이 **적재를 말할 길이
|
|
20
|
+
* 없었다.** 관측 리듀서는 `AggregationEvent` 를 받으면 자식 물품에 `parent` 를 붙이도록 이미 되어 있었는데
|
|
21
|
+
* (3D 가 「이 상자가 어느 팔레트에 실렸나」를 묻는 자리) 그 사실이 문 앞에서 사라졌다 — 시뮬 원본을
|
|
22
|
+
* 붙여 돌려 보고서야 그것이 로그로 드러났다(*"is not carried to the twin"*).
|
|
23
|
+
*
|
|
24
|
+
* 자식은 **두 가지로 말할 수 있다**(표준이 그렇고 우리 커널도 둘 다 낸다):
|
|
25
|
+
* · `childEPCs` — 개체 하나하나(상자 3개의 식별자).
|
|
26
|
+
* · `childQuantityList` — 클래스와 수량(「이 품번 40개」). 낱개 식별자가 없는 입고·포장이 이 모양이다.
|
|
27
|
+
* 하나만 받으면 나머지 사실이 문 앞에서 사라진다 — 실제로 그렇게 사라지고 있었다(로그가 짚었다).
|
|
28
|
+
*/
|
|
29
|
+
export interface AggregationEventMapping {
|
|
30
|
+
type: 'AggregationEvent';
|
|
31
|
+
action: MapValue;
|
|
32
|
+
bizStep: MapValue;
|
|
33
|
+
disposition?: MapValue;
|
|
34
|
+
parentID: MapValue;
|
|
35
|
+
/** 자식 목록 참조("$.childEPCs") — 문자열 배열이어야 한다. */
|
|
36
|
+
childEPCs?: MapValue;
|
|
37
|
+
/** 수량으로 담긴 자식 참조("$.childQuantityList") — `{ epcClass, quantity, uom? }` 배열. */
|
|
38
|
+
childQuantityList?: MapValue;
|
|
39
|
+
readPoint?: MapValue;
|
|
40
|
+
bizLocation?: MapValue;
|
|
41
|
+
}
|
|
42
|
+
export type EventMapping = ObjectEventMapping | AggregationEventMapping;
|
|
43
|
+
/**
|
|
44
|
+
* 이 품목 레코드가 **담김의 사실**인가 — 라우팅 판정을 한 곳에 둔다(소비처가 각자 짐작하지 않게).
|
|
45
|
+
*
|
|
46
|
+
* 담김은 부모와 자식으로 말한다(`parentID`·`childEPCs`). 개체 하나의 관측(`epc`)과 섞이지 않게, 부모가
|
|
47
|
+
* 있으면 담김으로 본다 — 둘 다 있으면 커넥터가 무엇을 말하려는지 알 수 없으므로 담김으로 받지 않는다.
|
|
48
|
+
*/
|
|
49
|
+
export declare function isAggregationRecord(record: unknown): boolean;
|
|
16
50
|
/** 룰: 소스 레코드의 판별자(sourceType)로 매핑을 고른다. */
|
|
17
51
|
export interface AdapterRule {
|
|
18
52
|
sourceType: string;
|
|
@@ -32,7 +66,12 @@ export interface IngestResult {
|
|
|
32
66
|
}[];
|
|
33
67
|
}
|
|
34
68
|
type Rec = Record<string, unknown>;
|
|
35
|
-
/** 단일 레코드 → 정규 EPCIS 이벤트(
|
|
69
|
+
/** 단일 레코드 → 정규 EPCIS 이벤트 + 매핑에서 드러난 문제(검증은 `ingest` 가 이어서 한다). */
|
|
70
|
+
export declare function mapRecordChecked(record: Rec, mapping: EventMapping, eventTime: string): {
|
|
71
|
+
event: EpcisEvent;
|
|
72
|
+
errors: string[];
|
|
73
|
+
};
|
|
74
|
+
/** 단일 레코드 → 정규 EPCIS 이벤트(매핑만). 매핑 문제까지 보려면 `mapRecordChecked`. */
|
|
36
75
|
export declare function mapRecord(record: Rec, mapping: EventMapping, eventTime: string): EpcisEvent;
|
|
37
76
|
/**
|
|
38
77
|
* 레거시 레코드 배열 → 정규 EPCIS 봉투. 매핑 후 검증, 실패분은 rejected(오염 차단).
|
package/dist/face2-adapter.js
CHANGED
|
@@ -8,32 +8,110 @@
|
|
|
8
8
|
* 매핑 엔진은 walking-skeleton 범위에서 무의존 declarative spec 사용.
|
|
9
9
|
* (jsonata/템플릿 엔진 최종 선택은 열린 결정 — face2-adapters.md. 커널 zero-dep 유지 위해 여기선 미도입.)
|
|
10
10
|
*/
|
|
11
|
-
import { objectEvent, validateEpcisEvent } from "./epcis.js";
|
|
11
|
+
import { aggregationEvent, objectEvent, validateEpcisEvent } from "./epcis.js";
|
|
12
|
+
/**
|
|
13
|
+
* 이 품목 레코드가 **담김의 사실**인가 — 라우팅 판정을 한 곳에 둔다(소비처가 각자 짐작하지 않게).
|
|
14
|
+
*
|
|
15
|
+
* 담김은 부모와 자식으로 말한다(`parentID`·`childEPCs`). 개체 하나의 관측(`epc`)과 섞이지 않게, 부모가
|
|
16
|
+
* 있으면 담김으로 본다 — 둘 다 있으면 커넥터가 무엇을 말하려는지 알 수 없으므로 담김으로 받지 않는다.
|
|
17
|
+
*/
|
|
18
|
+
export function isAggregationRecord(record) {
|
|
19
|
+
if (!record || typeof record !== 'object')
|
|
20
|
+
return false;
|
|
21
|
+
const r = record;
|
|
22
|
+
return typeof r.parentID === 'string' && r.parentID.trim().length > 0 && r.epc === undefined;
|
|
23
|
+
}
|
|
12
24
|
function get(obj, path) {
|
|
13
25
|
return path.split('.').reduce((o, k) => (o == null ? o : o[k]), obj);
|
|
14
26
|
}
|
|
15
|
-
/**
|
|
16
|
-
|
|
27
|
+
/**
|
|
28
|
+
* 매핑 값 해석: "$.path" 는 소스 참조, 그 외는 리터럴.
|
|
29
|
+
*
|
|
30
|
+
* ── 객체를 문자열로 굳히지 않는다 (2026-08-19 실측으로 고침) ────────────────
|
|
31
|
+
* 예전에는 무엇이 오든 `String(값)` 이었다. 그래서 커널이 낸 EPCIS 의 `readPoint`(= `{ id }`)를 이 문에
|
|
32
|
+
* 그대로 실은 커넥터가 있었고, **오류 하나 없이** `readPoint: { id: "[object Object]" }` 가 저널에 쌓였다
|
|
33
|
+
* (실 앱에서 관측). 자리를 잃은 관측은 「어디 있나」에 답하지 못하는데 화면은 값이 있다고 믿는다.
|
|
34
|
+
*
|
|
35
|
+
* 그러니 객체·배열은 **매핑 오류**다. 조용히 버리지도(그러면 자리가 없어진 이유를 아무도 모른다),
|
|
36
|
+
* 문자열로 굳히지도 않고, 그 레코드를 이유와 함께 거부한다.
|
|
37
|
+
*/
|
|
38
|
+
function resolve(v, record, field, errors) {
|
|
17
39
|
if (v === undefined)
|
|
18
40
|
return undefined;
|
|
19
|
-
if (v.startsWith('$.'))
|
|
20
|
-
|
|
21
|
-
|
|
41
|
+
if (!v.startsWith('$.'))
|
|
42
|
+
return v;
|
|
43
|
+
const r = get(record, v.slice(2));
|
|
44
|
+
if (r === undefined || r === null)
|
|
45
|
+
return undefined;
|
|
46
|
+
if (typeof r === 'object') {
|
|
47
|
+
errors.push(`${field} 가 값이 아니라 ${Array.isArray(r) ? '배열' : '객체'}로 왔다(${v}) — 커넥터가 표준 구조를 그대로 실었다: ${JSON.stringify(r).slice(0, 80)}`);
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return String(r);
|
|
51
|
+
}
|
|
52
|
+
/** 목록 참조 해석 — 문자열 배열만 받는다(하나만 온 것을 배열로 지어내지 않는다). */
|
|
53
|
+
function resolveList(v, record, field, errors) {
|
|
54
|
+
if (v === undefined)
|
|
55
|
+
return [];
|
|
56
|
+
const r = get(record, v.startsWith('$.') ? v.slice(2) : v);
|
|
57
|
+
if (r === undefined || r === null)
|
|
58
|
+
return [];
|
|
59
|
+
if (!Array.isArray(r) || r.some(x => typeof x !== 'string')) {
|
|
60
|
+
errors.push(`${field} 가 문자열 배열이 아니다(${v}): ${JSON.stringify(r).slice(0, 80)}`);
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
return r.slice();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 수량 목록 참조 해석 — `{ epcClass, quantity, uom? }` 배열.
|
|
67
|
+
*
|
|
68
|
+
* 값의 옳고 그름(클래스 식별자인가·수량이 양수인가)은 **검증이 본다**(`validateEpcisEvent`) — 여기서
|
|
69
|
+
* 다시 판정하면 규칙이 두 벌이 된다. 이 자리는 「배열인가·객체인가」만 지킨다.
|
|
70
|
+
*/
|
|
71
|
+
function resolveQuantityList(v, record, field, errors) {
|
|
72
|
+
if (v === undefined)
|
|
73
|
+
return [];
|
|
74
|
+
const r = get(record, v.startsWith('$.') ? v.slice(2) : v);
|
|
75
|
+
if (r === undefined || r === null)
|
|
76
|
+
return [];
|
|
77
|
+
if (!Array.isArray(r) || r.some(x => !x || typeof x !== 'object' || Array.isArray(x))) {
|
|
78
|
+
errors.push(`${field} 가 객체 배열이 아니다(${v}): ${JSON.stringify(r).slice(0, 80)}`);
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
return r.map(x => ({ ...x }));
|
|
82
|
+
}
|
|
83
|
+
/** 단일 레코드 → 정규 EPCIS 이벤트 + 매핑에서 드러난 문제(검증은 `ingest` 가 이어서 한다). */
|
|
84
|
+
export function mapRecordChecked(record, mapping, eventTime) {
|
|
85
|
+
const errors = [];
|
|
86
|
+
const action = (resolve(mapping.action, record, 'action', errors) ?? '');
|
|
87
|
+
const bizStep = resolve(mapping.bizStep, record, 'bizStep', errors) ?? '';
|
|
88
|
+
const disposition = resolve(mapping.disposition, record, 'disposition', errors);
|
|
89
|
+
const readPoint = resolve(mapping.readPoint, record, 'readPoint', errors);
|
|
90
|
+
const bizLocation = resolve(mapping.bizLocation, record, 'bizLocation', errors);
|
|
91
|
+
if (mapping.type === 'AggregationEvent') {
|
|
92
|
+
const parentID = resolve(mapping.parentID, record, 'parentID', errors) ?? '';
|
|
93
|
+
const childEPCs = resolveList(mapping.childEPCs, record, 'childEPCs', errors);
|
|
94
|
+
const childQuantityList = resolveQuantityList(mapping.childQuantityList, record, 'childQuantityList', errors);
|
|
95
|
+
return {
|
|
96
|
+
event: aggregationEvent({
|
|
97
|
+
eventTime, action, bizStep, disposition, parentID,
|
|
98
|
+
/* 없는 쪽은 **필드를 만들지 않는다** — 빈 배열을 실으면 「자식이 없다」고 말하는 것이 된다. */
|
|
99
|
+
...(childEPCs.length ? { childEPCs } : {}),
|
|
100
|
+
...(childQuantityList.length ? { childQuantityList } : {}),
|
|
101
|
+
readPoint, bizLocation
|
|
102
|
+
}),
|
|
103
|
+
errors
|
|
104
|
+
};
|
|
22
105
|
}
|
|
23
|
-
|
|
106
|
+
const epc = resolve(mapping.epc, record, 'epc', errors);
|
|
107
|
+
return {
|
|
108
|
+
event: objectEvent({ eventTime, action, bizStep, disposition, epcList: epc ? [epc] : [], readPoint, bizLocation }),
|
|
109
|
+
errors
|
|
110
|
+
};
|
|
24
111
|
}
|
|
25
|
-
/** 단일 레코드 → 정규 EPCIS 이벤트(
|
|
112
|
+
/** 단일 레코드 → 정규 EPCIS 이벤트(매핑만). 매핑 문제까지 보려면 `mapRecordChecked`. */
|
|
26
113
|
export function mapRecord(record, mapping, eventTime) {
|
|
27
|
-
|
|
28
|
-
return objectEvent({
|
|
29
|
-
eventTime,
|
|
30
|
-
action: (resolve(mapping.action, record) ?? ''),
|
|
31
|
-
bizStep: resolve(mapping.bizStep, record) ?? '',
|
|
32
|
-
disposition: resolve(mapping.disposition, record),
|
|
33
|
-
epcList: epc ? [epc] : [],
|
|
34
|
-
readPoint: resolve(mapping.readPoint, record),
|
|
35
|
-
bizLocation: resolve(mapping.bizLocation, record)
|
|
36
|
-
});
|
|
114
|
+
return mapRecordChecked(record, mapping, eventTime).event;
|
|
37
115
|
}
|
|
38
116
|
/**
|
|
39
117
|
* 레거시 레코드 배열 → 정규 EPCIS 봉투. 매핑 후 검증, 실패분은 rejected(오염 차단).
|
|
@@ -50,9 +128,11 @@ export function ingest(records, rules, opts) {
|
|
|
50
128
|
rejected.push({ record, errors: [`매칭 룰 없음: sourceType=${record['sourceType']}`] });
|
|
51
129
|
continue;
|
|
52
130
|
}
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const errors =
|
|
131
|
+
const timeErrors = [];
|
|
132
|
+
const eventTime = (opts.eventTimePath ? resolve('$.' + opts.eventTimePath, record, 'eventTime', timeErrors) : undefined) ?? opts.defaultEventTime;
|
|
133
|
+
const { event: ev, errors: mapErrors } = mapRecordChecked(record, rule.mapping, eventTime);
|
|
134
|
+
/* 매핑에서 드러난 문제를 검증 위반과 **같은 자리**에 담는다 — 부르는 쪽은 이유를 한 곳에서 읽는다. */
|
|
135
|
+
const errors = [...timeErrors, ...mapErrors, ...validateEpcisEvent(ev)];
|
|
56
136
|
if (errors.length) {
|
|
57
137
|
rejected.push({ record, errors });
|
|
58
138
|
continue;
|
package/dist-cjs/index.cjs
CHANGED
|
@@ -118,6 +118,7 @@ __export(index_exports, {
|
|
|
118
118
|
ingestEnergyEquipmentRecords: () => ingestEnergyEquipmentRecords,
|
|
119
119
|
ingestEnergyRecords: () => ingestEnergyRecords,
|
|
120
120
|
ingestOperationalRecords: () => ingestOperationalRecords,
|
|
121
|
+
isAggregationRecord: () => isAggregationRecord,
|
|
121
122
|
isElectricalLocationType: () => isElectricalLocationType,
|
|
122
123
|
isEnergyEquipmentRecord: () => isEnergyEquipmentRecord,
|
|
123
124
|
isEnergyRecord: () => isEnergyRecord,
|
|
@@ -129,6 +130,7 @@ __export(index_exports, {
|
|
|
129
130
|
lgtinClass: () => lgtinClass,
|
|
130
131
|
locationStatusOf: () => locationStatusOf,
|
|
131
132
|
mapRecord: () => mapRecord,
|
|
133
|
+
mapRecordChecked: () => mapRecordChecked,
|
|
132
134
|
meetsTests: () => meetsTests,
|
|
133
135
|
minuteOfDayAt: () => minuteOfDayAt,
|
|
134
136
|
monteCarloForecast: () => monteCarloForecast,
|
|
@@ -2684,28 +2686,80 @@ function foldJobResponses(rows) {
|
|
|
2684
2686
|
}
|
|
2685
2687
|
|
|
2686
2688
|
// src/face2-adapter.ts
|
|
2689
|
+
function isAggregationRecord(record) {
|
|
2690
|
+
if (!record || typeof record !== "object") return false;
|
|
2691
|
+
const r = record;
|
|
2692
|
+
return typeof r.parentID === "string" && r.parentID.trim().length > 0 && r.epc === void 0;
|
|
2693
|
+
}
|
|
2687
2694
|
function get(obj, path) {
|
|
2688
2695
|
return path.split(".").reduce((o, k) => o == null ? o : o[k], obj);
|
|
2689
2696
|
}
|
|
2690
|
-
function resolve(v, record) {
|
|
2697
|
+
function resolve(v, record, field, errors) {
|
|
2691
2698
|
if (v === void 0) return void 0;
|
|
2692
|
-
if (v.startsWith("$."))
|
|
2693
|
-
|
|
2694
|
-
|
|
2699
|
+
if (!v.startsWith("$.")) return v;
|
|
2700
|
+
const r = get(record, v.slice(2));
|
|
2701
|
+
if (r === void 0 || r === null) return void 0;
|
|
2702
|
+
if (typeof r === "object") {
|
|
2703
|
+
errors.push(`${field} \uAC00 \uAC12\uC774 \uC544\uB2C8\uB77C ${Array.isArray(r) ? "\uBC30\uC5F4" : "\uAC1D\uCCB4"}\uB85C \uC654\uB2E4(${v}) \u2014 \uCEE4\uB125\uD130\uAC00 \uD45C\uC900 \uAD6C\uC870\uB97C \uADF8\uB300\uB85C \uC2E4\uC5C8\uB2E4: ${JSON.stringify(r).slice(0, 80)}`);
|
|
2704
|
+
return void 0;
|
|
2695
2705
|
}
|
|
2696
|
-
return
|
|
2706
|
+
return String(r);
|
|
2707
|
+
}
|
|
2708
|
+
function resolveList(v, record, field, errors) {
|
|
2709
|
+
if (v === void 0) return [];
|
|
2710
|
+
const r = get(record, v.startsWith("$.") ? v.slice(2) : v);
|
|
2711
|
+
if (r === void 0 || r === null) return [];
|
|
2712
|
+
if (!Array.isArray(r) || r.some((x) => typeof x !== "string")) {
|
|
2713
|
+
errors.push(`${field} \uAC00 \uBB38\uC790\uC5F4 \uBC30\uC5F4\uC774 \uC544\uB2C8\uB2E4(${v}): ${JSON.stringify(r).slice(0, 80)}`);
|
|
2714
|
+
return [];
|
|
2715
|
+
}
|
|
2716
|
+
return r.slice();
|
|
2717
|
+
}
|
|
2718
|
+
function resolveQuantityList(v, record, field, errors) {
|
|
2719
|
+
if (v === void 0) return [];
|
|
2720
|
+
const r = get(record, v.startsWith("$.") ? v.slice(2) : v);
|
|
2721
|
+
if (r === void 0 || r === null) return [];
|
|
2722
|
+
if (!Array.isArray(r) || r.some((x) => !x || typeof x !== "object" || Array.isArray(x))) {
|
|
2723
|
+
errors.push(`${field} \uAC00 \uAC1D\uCCB4 \uBC30\uC5F4\uC774 \uC544\uB2C8\uB2E4(${v}): ${JSON.stringify(r).slice(0, 80)}`);
|
|
2724
|
+
return [];
|
|
2725
|
+
}
|
|
2726
|
+
return r.map((x) => ({ ...x }));
|
|
2727
|
+
}
|
|
2728
|
+
function mapRecordChecked(record, mapping, eventTime) {
|
|
2729
|
+
const errors = [];
|
|
2730
|
+
const action = resolve(mapping.action, record, "action", errors) ?? "";
|
|
2731
|
+
const bizStep = resolve(mapping.bizStep, record, "bizStep", errors) ?? "";
|
|
2732
|
+
const disposition = resolve(mapping.disposition, record, "disposition", errors);
|
|
2733
|
+
const readPoint = resolve(mapping.readPoint, record, "readPoint", errors);
|
|
2734
|
+
const bizLocation = resolve(mapping.bizLocation, record, "bizLocation", errors);
|
|
2735
|
+
if (mapping.type === "AggregationEvent") {
|
|
2736
|
+
const parentID = resolve(mapping.parentID, record, "parentID", errors) ?? "";
|
|
2737
|
+
const childEPCs = resolveList(mapping.childEPCs, record, "childEPCs", errors);
|
|
2738
|
+
const childQuantityList = resolveQuantityList(mapping.childQuantityList, record, "childQuantityList", errors);
|
|
2739
|
+
return {
|
|
2740
|
+
event: aggregationEvent({
|
|
2741
|
+
eventTime,
|
|
2742
|
+
action,
|
|
2743
|
+
bizStep,
|
|
2744
|
+
disposition,
|
|
2745
|
+
parentID,
|
|
2746
|
+
/* 없는 쪽은 **필드를 만들지 않는다** — 빈 배열을 실으면 「자식이 없다」고 말하는 것이 된다. */
|
|
2747
|
+
...childEPCs.length ? { childEPCs } : {},
|
|
2748
|
+
...childQuantityList.length ? { childQuantityList } : {},
|
|
2749
|
+
readPoint,
|
|
2750
|
+
bizLocation
|
|
2751
|
+
}),
|
|
2752
|
+
errors
|
|
2753
|
+
};
|
|
2754
|
+
}
|
|
2755
|
+
const epc = resolve(mapping.epc, record, "epc", errors);
|
|
2756
|
+
return {
|
|
2757
|
+
event: objectEvent({ eventTime, action, bizStep, disposition, epcList: epc ? [epc] : [], readPoint, bizLocation }),
|
|
2758
|
+
errors
|
|
2759
|
+
};
|
|
2697
2760
|
}
|
|
2698
2761
|
function mapRecord(record, mapping, eventTime) {
|
|
2699
|
-
|
|
2700
|
-
return objectEvent({
|
|
2701
|
-
eventTime,
|
|
2702
|
-
action: resolve(mapping.action, record) ?? "",
|
|
2703
|
-
bizStep: resolve(mapping.bizStep, record) ?? "",
|
|
2704
|
-
disposition: resolve(mapping.disposition, record),
|
|
2705
|
-
epcList: epc ? [epc] : [],
|
|
2706
|
-
readPoint: resolve(mapping.readPoint, record),
|
|
2707
|
-
bizLocation: resolve(mapping.bizLocation, record)
|
|
2708
|
-
});
|
|
2762
|
+
return mapRecordChecked(record, mapping, eventTime).event;
|
|
2709
2763
|
}
|
|
2710
2764
|
function ingest(records, rules, opts) {
|
|
2711
2765
|
const ruleByType = new Map(rules.map((r) => [r.sourceType, r]));
|
|
@@ -2718,9 +2772,10 @@ function ingest(records, rules, opts) {
|
|
|
2718
2772
|
rejected.push({ record, errors: [`\uB9E4\uCE6D \uB8F0 \uC5C6\uC74C: sourceType=${record["sourceType"]}`] });
|
|
2719
2773
|
continue;
|
|
2720
2774
|
}
|
|
2721
|
-
const
|
|
2722
|
-
const
|
|
2723
|
-
const errors =
|
|
2775
|
+
const timeErrors = [];
|
|
2776
|
+
const eventTime = (opts.eventTimePath ? resolve("$." + opts.eventTimePath, record, "eventTime", timeErrors) : void 0) ?? opts.defaultEventTime;
|
|
2777
|
+
const { event: ev, errors: mapErrors } = mapRecordChecked(record, rule.mapping, eventTime);
|
|
2778
|
+
const errors = [...timeErrors, ...mapErrors, ...validateEpcisEvent(ev)];
|
|
2724
2779
|
if (errors.length) {
|
|
2725
2780
|
rejected.push({ record, errors });
|
|
2726
2781
|
continue;
|
|
@@ -7762,6 +7817,7 @@ function retiredVocabularyIn(line) {
|
|
|
7762
7817
|
ingestEnergyEquipmentRecords,
|
|
7763
7818
|
ingestEnergyRecords,
|
|
7764
7819
|
ingestOperationalRecords,
|
|
7820
|
+
isAggregationRecord,
|
|
7765
7821
|
isElectricalLocationType,
|
|
7766
7822
|
isEnergyEquipmentRecord,
|
|
7767
7823
|
isEnergyRecord,
|
|
@@ -7773,6 +7829,7 @@ function retiredVocabularyIn(line) {
|
|
|
7773
7829
|
lgtinClass,
|
|
7774
7830
|
locationStatusOf,
|
|
7775
7831
|
mapRecord,
|
|
7832
|
+
mapRecordChecked,
|
|
7776
7833
|
meetsTests,
|
|
7777
7834
|
minuteOfDayAt,
|
|
7778
7835
|
monteCarloForecast,
|
package/package.json
CHANGED