Files
Aislo/M02_MasterTemplete/DataFlow/M02_DF_Detail.ts
T

137 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* M02_DF_Detail.ts
* 상세 내용 판(선 · 알약) 공용 — 값 읽기 · 규칙 미리 보기 · 끊김 글 · 흐름 줄 · DOM 없음
* 셈은 모델 것 그대로(Eval 의 evalLogic · breaks · Model 의 traceBack · traceForward · Rule 의 ruleLabel)
* 글 [한, 영] 짝은 아래 TEXT
* ========================================================================== */
import { currentLanguageIndex } from "@ui/ui_template_locale";
import {
breaks,
evalLogic,
type CfBreak,
type CfEvalOptions,
type CfEvalResult,
type CfValue,
type CfValues,
} from "./M02_DF_Eval";
import {
portDir,
portsOut,
traceBack,
traceForward,
type CfBox,
type CfGateOp,
type CfLink,
type CfLogic,
type CfPort,
type CfRule,
type CfStep,
} from "./M02_DF_Model";
import { ruleLabel } from "./M02_DF_Rule";
const TEXT = {
Table: ["표 {n}줄", "table, {n} rows"],
Empty: ["값 없음", "no value"],
NoRule: ["규칙 없음 — 값이 그대로 감", "No rule — the value goes as is"],
} as const;
export const td = (key: keyof typeof TEXT, fill: Record<string, string | number> = {}): string =>
Object.entries(fill).reduce(
(s, [k, v]) => s.replace(`{${k}}`, String(v)),
TEXT[key][currentLanguageIndex === 1 ? 1 : 0] as string,
);
export const GATE_OPS: CfGateOp[] = ["=", "≠", "≥", "≤", ">", "<"];
export const SCALE_OPS: ("×" | "÷" | "+%" | "-%")[] = ["×", "÷", "+%", "-%"];
/** 셈에 넘길 것 — 값 묶음 · 다른 로직들 */
export interface DetailOptions {
values?: CfValues;
logics?: CfEvalOptions["logics"];
}
export const boxOf = (lg: CfLogic, id: string): CfBox | undefined =>
lg.boxes.find((b) => b.id === id);
export const portText = (lg: CfLogic, p: CfPort): string =>
`${boxOf(lg, p.box)?.name ?? "?"} › ${p.name}`;
/** 계산 한 번 — 한창 만드는 중 막히면 null */
export function evalSafe(lg: CfLogic, o: DetailOptions = {}): CfEvalResult | null {
try {
return evalLogic(lg, o.values, { logics: o.logics });
} catch {
return null;
}
}
export const valueOf = (ev: CfEvalResult | null, p: CfPort): CfValue | undefined =>
ev?.ports[p.box]?.[p.name];
/** 화면에 보일 값 글 — 표는 줄 수 */
export function showValue(v: CfValue | undefined): string {
if (v === undefined || v === null) return td("Empty");
if (Array.isArray(v)) return td("Table", { n: Math.max(0, v.length - 1) });
return typeof v === "number" ? String(Number(v.toPrecision(12))) : String(v);
}
export function unitOf(lg: CfLogic, p: CfPort): string {
const box = boxOf(lg, p.box);
return (box && portsOut(box).find((x) => x.name === p.name)?.unit) || box?.unit || "-";
}
/** 알약이 나가는(out) 쪽인가 */
export const isOut = (box: CfBox, name: string): boolean => portDir(box, name) === "out";
/** 선 위 규칙 글 */
export const labelOf = (rule: CfRule | undefined): string =>
rule ? ruleLabel(rule) : td("NoRule");
/** 지나가기 전 값 → 지난 뒤 값 — 지난 뒤 값은 셈이 받는 알약에 실제로 들여보낸 것 · 못 들어갔으면 끊긴 까닭 */
export function preview(link: CfLink, ev: CfEvalResult | null): { before: string; after: string } {
const before = showValue(valueOf(ev, link.from));
if (!link.rule) return { before, after: before };
const arrived = ev?.arrived?.[link.to.box]?.[link.to.name];
if (arrived !== undefined && arrived !== null) return { before, after: showValue(arrived) };
const why = ev?.breaks?.find((b) => b.link === link.id)?.text;
return { before, after: why ?? td("Empty") };
}
/** 이 선에서 알릴 것 — 이 선이 막힌 까닭 · 같은 알약에서 나간 조건 선의 겹침 · 안 맞음 */
export function linkNotes(link: CfLink, ev: CfEvalResult | null): string[] {
return (ev?.breaks ?? [])
.filter(
(b) =>
b.link === link.id ||
((b.kind === "조건 겹침" || b.kind === "조건 안 맞음") &&
b.box === link.from.box &&
b.port === link.from.name),
)
.map((b) => b.text);
}
/** 온 길(dir "in") · 가는 길(dir "out") — 모델 것에서 자기 자신 줄은 뺌 */
export function trace(lg: CfLogic, p: CfPort, dir: "in" | "out"): CfStep[] {
const steps = dir === "in" ? traceBack(lg, p) : traceForward(lg, p);
return steps.filter((s) => !(s.box === p.box && s.port === p.name));
}
/** 그 상자(알약)에서 끊긴 곳 — 쉬운 말 */
export function breakLines(
lg: CfLogic,
boxId: string,
name?: string,
o: DetailOptions = {},
): string[] {
let list: CfBreak[] = [];
try {
list = breaks(lg, o.values, { logics: o.logics });
} catch {
// 만드는 중 셈이 막히면 끊긴 곳 없음으로 둠
}
return list
.filter((b) => b.box === boxId && (!name || !b.port || b.port === name))
.map((b) => b.text);
}