Files
Aislo/tools/stmate-test/scr_jdl.js
T
eomsangdonandClaude Opus 5.5 47a04b6df8 feat(stmate-test): REA MCP(Ghidra) 파일 입출력 — JDL 탭 줄·묶음 내보내기·검사 · 구버전→Win8 변환 · 엑셀 역등록 구성 줄 · 자재 집계표 엑셀 · 그림 코드
- JDL 줄 = 탭 구분 + \x05/\x07 덧붙임(검사기 0x504d20 · 고정폭 표는 죽은 고리 — 34 §17.9 바로잡음) · 단가 목록 7종·Main·GongJong 머리·Global.Ini 상수 키 · zip 받기
- 구버전 판 가리기(Chk_Data 0x459280) · 7.x/DOS 표별 필드 대응 · B→X 조건 = 단위 X
- 엑셀 역등록 PRPT「속성」·PRIN「대상」·PROUT「저장」 = PR/RR 줄만 · 원본 BDQTY 4건 일치
- 자재 = 재료비 수량집계표 엑셀 내보내기(주요건설자재현황 시트)
- 그림 코드 = COSTN I 코드 IFORM · SIZE = 그림 클래스 이름
- 지식DB 37번

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 15:58:17 +09:00

110 lines
3.6 KiB
JavaScript

/**
* STMATE w 8.0 — 조달청포맷(TEXT,JDL) 생성·검사 끼워 넣기 (scr_jdl.js) · 1번(파일) 메뉴
* 원본: ST_U_Convert Conv_ST8_Jodal(「조달청포맷(TEXT,JDL) 생성」) · JDLCHK.Tab_33.dll JDLCheck
* 서버: api_jdl.py — 묶음 요약 · zip 받기 · 받은 묶음 검사(열 개수 · 코드 글자)
*/
(function (global) {
"use strict";
const PLUG = (global.STMATE_PLUGINS = global.STMATE_PLUGINS || {
screens: {},
actions: {},
menus: [],
});
const app = () => global.STMATE_APP;
async function req(url, body, raw) {
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!res.ok) throw new Error(await res.text());
return raw ? res.blob() : res.json();
}
function curPath(ctx) {
if (!ctx.currentSourcePath) throw new Error("먼저 프로젝트를 불러오세요.");
return ctx.currentSourcePath;
}
function download(blob, name) {
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = name;
document.body.appendChild(a);
a.click();
setTimeout(() => (URL.revokeObjectURL(a.href), a.remove()), 1000);
}
function pickFiles() {
return new Promise((resolve, reject) => {
const inp = document.createElement("input");
inp.type = "file";
inp.multiple = true;
inp.accept = ".txt,.TXT";
inp.onchange = async () => {
const list = Array.from(inp.files || []);
if (!list.length) return reject(new Error("파일을 고르지 않음"));
const out = {};
for (const f of list)
out[f.name] = await new Promise((ok, no) => {
const rd = new FileReader();
rd.onload = () => ok(String(rd.result));
rd.onerror = () => no(rd.error);
rd.readAsDataURL(f);
});
resolve(out);
};
inp.click();
});
}
function summary(r) {
const lines = Object.entries(r.counts).map(([k, v]) => {
const e = (r.checks[k] || []).length;
return `${k}: ${v}줄${e ? ` · 오류 ${e}` : ""}`;
});
const warn = r.warnings.length ? `\n\n[알림]\n${r.warnings.slice(0, 8).join("\n")}` : "";
return (
`계열 ${r.series} (PCLASS ${r.pclass})\n${lines.join("\n")}` +
`${warn}\n\n[아직 못 만드는 것 — 원본 규칙 미확인]\n${r.missing.join("\n")}`
);
}
const ACTS = {
async jdlExport(ctx) {
const body = { source_path: curPath(ctx) };
const r = await req("/api/jdl/export", body);
if (!confirm(`[조달청포맷(TEXT,JDL) 생성]\n${summary(r)}\n\n묶음 파일을 zip 으로 받을까요?`))
return;
const name = (ctx.state.projectMeta.pname || "공사").slice(0, 28);
download(await req("/api/jdl/export.zip", body, true), `${name}_JDL_TEXT.zip`);
},
async jdlCheck() {
const files = await pickFiles();
const r = await req("/api/jdl/check", { files });
const text = Object.entries(r.checks)
.map(([k, v]) => `${k}: ${v.length ? v.slice(0, 20).join("\n ") : "통과"}`)
.join("\n");
alert(`[JDL 파일 오류검사]\n${text}`);
},
};
async function run(act) {
try {
await ACTS[act](app().ctx);
} catch (err) {
alert("JDL 처리 실패: " + err.message);
}
}
[
["Act_JdlExport", "조달청포맷(TEXT,JDL) 생성", "jdlExport"],
["Act_JdlCheck", "JDL 파일 오류검사 (JDLCheck)", "jdlCheck"],
].forEach(([act, label, key]) => {
PLUG.actions[act] = () => run(key);
PLUG.menus.push({ menu: "m1", label, act });
});
})(window);