fix(m02-df): 변환 흐름 화면 흠 넷 — 변수 예시값 · 표 찾기 실패 같은 뜻 · 다른 로직 서버 값 · 합계 끝수
- 변환: 설계 값 변수 예시값 = 줄 시험입력 · 없으면 check_master.시험입력 기본(글 수 → 수) · totalRound = null(옛 계는 끝수 없음) - Model CfLogic.totalRound(없음 = 기본 계금 1원 미만 버림 · null = 끝수 없음) · Round totalRoundFor · roundBy/roundText 가 null 받음 · Summary · M01 흐름 보기 합계가 그것을 씀 - Compute: 함수 결과 오류는 오류 값(「#N/A」 등)으로 넘김(화면 Eval 과 같음 · 받는 칸으로 번짐 · 옛 엔진은 그 줄을 비움) — 서버가 빈 칸 0 으로 계속 세던 차이 없앰 - 화면 값 물음(Entry_Detail queriesOf): 인력 · 기계 · 유가 조건 찾기도 자리 물음 · 재료 검색어 틀은 채운 글 · 다른 로직 입력은 서버 /logic-value 로 받은 설계 값째 셈(그 안 조건 찾기 · 단가까지) · Eval 은 서버 출력이 있으면 그것 - Values.one_value: 인력 · 기계 · 유가 자리 물음 = group_find - 전수 대조 시험입력 1,414/1,472(96.1%) · 기본 입력 1,429(97.1%) — 남은 49 는 sub4 범위규칙 칸 · 검색어 찾기 · 재료 값 차이 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VBToTCGk5dqDKeXCEP3vhk
This commit is contained in:
@@ -169,8 +169,24 @@ export async function unitChoices(): Promise<string[]> {
|
||||
return units.length ? units : ["원/㎡"];
|
||||
}
|
||||
|
||||
/** 재료 검색어 틀(「레미콘 {a}-{b}」)의 자리에 알약 값 — 빈 자리가 있으면 null(서버 Compute `_fill_template` 과 같음) */
|
||||
function fillTemplate(
|
||||
rule: NonNullable<CfElementRef["rule"]>,
|
||||
pill: (p: CfPort) => unknown,
|
||||
): unknown {
|
||||
const got = new Map((rule.slots ?? []).map((x) => [x.name, pill(x.from)]));
|
||||
if ([...got.values()].some((v) => v === null || v === undefined)) return null;
|
||||
return (rule.template ?? "").replace(/\{([^{}]+)\}/g, (m, k: string) =>
|
||||
got.has(k) ? String(got.get(k)) : m,
|
||||
);
|
||||
}
|
||||
|
||||
/** 조건 찾기를 쓰는 그룹 — 재료 밖은 인력 · 기계 · 유가전력(field 키 · 서버 Values.GROUP_RULE) */
|
||||
const RULE_GROUPS = ["인력", "기계", "유가전력"];
|
||||
|
||||
/** 입력 값 물음 — 그룹 입력 상자의 요소(알약 = 요소 이름) · 재료 자리(알약 = 자리 이름 · 조건 찾기는 이어진 알약 값을
|
||||
* pill 로 읽어 넘김) · 표(알약 「표」) · 사용자 요소 · 변수는 Eval 이 스스로 봄 */
|
||||
* pill 로 읽어 넘김 · 인력 · 기계 · 유가 조건 찾기도) · 표(알약 「표」) · 다른 로직(서버가 받은 설계 값으로 셈 · port 「*」)
|
||||
* · 사용자 요소 · 변수는 Eval 이 스스로 봄 */
|
||||
export function queriesOf(
|
||||
lg: CfLogic,
|
||||
pill: (p: CfPort) => unknown = () => null,
|
||||
@@ -178,10 +194,25 @@ export function queriesOf(
|
||||
): { q: CfQuery; box: string; port: string }[] {
|
||||
return lg.boxes.flatMap((b) => {
|
||||
const inp = b.input;
|
||||
if (b.kind === "input" && inp?.mode === "logic" && inp.logic?.key) {
|
||||
const ref = inp.logic;
|
||||
const designs: Record<string, unknown> = {};
|
||||
for (const d of ref.designs) {
|
||||
const l = lg.links.find((x) => x.to.box === b.id && x.to.name === d.name);
|
||||
const v = l ? pill(l.from) : ref.examples?.[d.name];
|
||||
if (v !== null && v !== undefined) designs[d.name] = v;
|
||||
}
|
||||
const q: CfQuery = { id: `${b.id}*`, group: "기계", logic: { key: ref.key, designs } };
|
||||
return [{ q, box: b.id, port: "*" }];
|
||||
}
|
||||
if (b.kind !== "input" || !inp || inp.mode !== "group" || !inp.group) return [];
|
||||
const group = inp.group;
|
||||
const slots = inp.elements
|
||||
.filter((e) => group === "재료" && e.mode && e.mode !== "fixed")
|
||||
.filter(
|
||||
(e) =>
|
||||
(group === "재료" && e.mode && e.mode !== "fixed") ||
|
||||
(RULE_GROUPS.includes(group) && e.mode === "rule"),
|
||||
)
|
||||
.map((e) => {
|
||||
const port = elementPort(e);
|
||||
const q: CfQuery = {
|
||||
@@ -190,7 +221,8 @@ export function queriesOf(
|
||||
unit: e.unit,
|
||||
slot: { ...e, field: e.rule?.field, op: e.rule?.op },
|
||||
};
|
||||
if (e.mode === "rule" && e.rule) q.value = pill(e.rule.from);
|
||||
if (e.mode === "rule" && e.rule)
|
||||
q.value = e.rule.field === "검색어" ? fillTemplate(e.rule, pill) : pill(e.rule.from);
|
||||
return { q, box: b.id, port };
|
||||
});
|
||||
const els = inp.elements
|
||||
@@ -216,14 +248,35 @@ export async function fetchValues(
|
||||
pill?: (p: CfPort) => unknown,
|
||||
elementPort?: (e: CfElementRef) => string,
|
||||
): Promise<{ values: CfValues; errors: string[] }> {
|
||||
const asks = queriesOf(lg, pill, elementPort);
|
||||
if (!asks.length) return { values: {}, errors: [] };
|
||||
const all = queriesOf(lg, pill, elementPort);
|
||||
const asks = all.filter((a) => !a.q.logic);
|
||||
const values: CfValues = {};
|
||||
const errors: string[] = [];
|
||||
// 다른 로직 — 서버가 받은 설계 값으로 셈(그 안 조건 찾기 · 단가까지) → 출력 이름마다 값
|
||||
const logicAsks = all.filter((a) => a.q.logic);
|
||||
const logicDone = logicAsks.length
|
||||
? import("./M02_DF_Api").then(({ cfLogicValue }) =>
|
||||
Promise.all(
|
||||
logicAsks.map((a) =>
|
||||
cfLogicValue(a.q.logic!.key, a.q.logic!.designs).then(
|
||||
(got) => {
|
||||
values[a.box] = { ...(values[a.box] ?? {}), ...(got.outputs as CfValues[string]) };
|
||||
},
|
||||
(e: unknown) => errors.push(`${a.q.logic!.key} — ${String(e)}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Promise.resolve();
|
||||
if (!asks.length) {
|
||||
await logicDone;
|
||||
return { values, errors };
|
||||
}
|
||||
const answers = await ask(
|
||||
asks.map((a) => a.q),
|
||||
lg.userElements as never,
|
||||
);
|
||||
const values: CfValues = {};
|
||||
const errors: string[] = [];
|
||||
await logicDone;
|
||||
for (const a of asks) {
|
||||
const got = answers.find((x) => x.id === a.q.id);
|
||||
if (!got?.ok) errors.push(`${a.port}${got?.reason ? ` — ${got.reason}` : ""}`);
|
||||
|
||||
Reference in New Issue
Block a user