596 lines
33 KiB
JavaScript
596 lines
33 KiB
JavaScript
import fs from "node:fs";
|
||
import path from "node:path";
|
||
import {
|
||
AlignmentType,
|
||
BorderStyle,
|
||
Document,
|
||
Footer,
|
||
Header,
|
||
HeadingLevel,
|
||
ImportedXmlComponent,
|
||
Packer,
|
||
PageBreak,
|
||
PageNumber,
|
||
Paragraph,
|
||
ShadingType,
|
||
Table,
|
||
TableCell,
|
||
TableRow,
|
||
TextRun,
|
||
WidthType,
|
||
convertInchesToTwip,
|
||
} from "docx";
|
||
|
||
const outputPath = process.argv[2];
|
||
if (!outputPath) {
|
||
throw new Error("Usage: node build-manual-docx.js /absolute/path/output.docx");
|
||
}
|
||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||
|
||
const T = String.raw;
|
||
|
||
const palette = {
|
||
dark: "1F2A36",
|
||
primary: "2F4858",
|
||
light: "6B7A86",
|
||
border: "8C979F",
|
||
fill: "F2F5F7",
|
||
};
|
||
|
||
const fontBody = {
|
||
ascii: "Times New Roman",
|
||
hAnsi: "Times New Roman",
|
||
cs: "Times New Roman",
|
||
eastAsia: "SimSun",
|
||
};
|
||
const fontHead = {
|
||
ascii: "Times New Roman",
|
||
hAnsi: "Times New Roman",
|
||
cs: "Times New Roman",
|
||
eastAsia: "SimHei",
|
||
};
|
||
const fontCode = {
|
||
ascii: "Consolas",
|
||
hAnsi: "Consolas",
|
||
cs: "Consolas",
|
||
eastAsia: "SimSun",
|
||
};
|
||
|
||
const run = (text, options = {}) =>
|
||
new TextRun({ text, font: fontBody, size: 24, ...options });
|
||
|
||
const para = (children, options = {}) =>
|
||
new Paragraph({
|
||
spacing: { after: 120, line: 360 },
|
||
...options,
|
||
children: Array.isArray(children) ? children : [children],
|
||
});
|
||
|
||
// 正文:小四宋体,首行缩进两字符,1.5 倍行距
|
||
const body = (text) =>
|
||
para(run(text), {
|
||
indent: { firstLine: 480 },
|
||
alignment: AlignmentType.JUSTIFIED,
|
||
});
|
||
|
||
// 章标题:三号黑体;节标题:四号黑体;小节:小四黑体
|
||
const h1 = (text) =>
|
||
para(new TextRun({ text, font: fontHead, bold: true, size: 32, color: palette.dark }), {
|
||
heading: HeadingLevel.HEADING_1,
|
||
spacing: { before: 360, after: 240 },
|
||
});
|
||
const h2 = (text) =>
|
||
para(new TextRun({ text, font: fontHead, bold: true, size: 28, color: palette.dark }), {
|
||
heading: HeadingLevel.HEADING_2,
|
||
spacing: { before: 280, after: 160 },
|
||
});
|
||
const h3 = (text) =>
|
||
para(new TextRun({ text, font: fontHead, bold: true, size: 24, color: palette.dark }), {
|
||
heading: HeadingLevel.HEADING_3,
|
||
spacing: { before: 200, after: 120 },
|
||
});
|
||
|
||
const bullet = (text) =>
|
||
para(run(text), { bullet: { level: 0 }, indent: { left: 480 } });
|
||
|
||
const note = (text) =>
|
||
para(run(text, { italics: true, color: palette.light, size: 21 }), {
|
||
indent: { firstLine: 480 },
|
||
});
|
||
|
||
const codeBlock = (lines) =>
|
||
lines.map(
|
||
(line) =>
|
||
new Paragraph({
|
||
spacing: { after: 40, line: 280 },
|
||
indent: { left: 480 },
|
||
shading: { type: ShadingType.CLEAR, fill: palette.fill },
|
||
children: [
|
||
new TextRun({ text: line === "" ? " " : line, font: fontCode, size: 20 }),
|
||
],
|
||
})
|
||
);
|
||
|
||
// 学术三线表:顶线、栏目线、底线,无竖线与内部横线
|
||
const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
|
||
const table3 = (headers, rows, widths) => {
|
||
const total = widths.reduce((a, b) => a + b, 0);
|
||
const cellPara = (text, bold) =>
|
||
new Paragraph({
|
||
spacing: { after: 40, line: 300 },
|
||
children: [
|
||
new TextRun({ text, font: bold ? fontHead : fontBody, size: 21, bold: !!bold }),
|
||
],
|
||
});
|
||
const mkCell = (text, i, opts = {}) =>
|
||
new TableCell({
|
||
children: [cellPara(text, opts.bold)],
|
||
width: { size: widths[i], type: WidthType.DXA },
|
||
margins: { top: 80, bottom: 80, left: 100, right: 100 },
|
||
borders: opts.borders,
|
||
});
|
||
const headerCells = headers.map((text, i) =>
|
||
mkCell(text, i, {
|
||
bold: true,
|
||
borders: {
|
||
top: { style: BorderStyle.SINGLE, size: 12, color: palette.dark },
|
||
bottom: { style: BorderStyle.SINGLE, size: 6, color: palette.dark },
|
||
left: noBorder,
|
||
right: noBorder,
|
||
},
|
||
})
|
||
);
|
||
const bodyRows = rows.map((row, ri) => {
|
||
const isLast = ri === rows.length - 1;
|
||
return new TableRow({
|
||
children: row.map((text, i) =>
|
||
mkCell(String(text), i, {
|
||
borders: {
|
||
top: noBorder,
|
||
bottom: isLast
|
||
? { style: BorderStyle.SINGLE, size: 12, color: palette.dark }
|
||
: noBorder,
|
||
left: noBorder,
|
||
right: noBorder,
|
||
},
|
||
})
|
||
),
|
||
});
|
||
});
|
||
return new Table({
|
||
width: { size: total, type: WidthType.DXA },
|
||
columnWidths: widths,
|
||
rows: [new TableRow({ children: headerCells, tableHeader: true }), ...bodyRows],
|
||
});
|
||
};
|
||
|
||
const caption = (text) =>
|
||
para(new TextRun({ text, font: fontHead, size: 21, bold: true, color: palette.primary }), {
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { before: 120, after: 80 },
|
||
});
|
||
|
||
const xmlEscape = (value) =>
|
||
String(value)
|
||
.replaceAll("&", "&")
|
||
.replaceAll("<", "<")
|
||
.replaceAll(">", ">")
|
||
.replaceAll('"', """);
|
||
|
||
const toc = (entries) => {
|
||
const cached = entries
|
||
.map(({ title: entryTitle, level, page }) => {
|
||
const indent = Math.max(0, level - 1) * 360;
|
||
return `<w:p>
|
||
<w:pPr>
|
||
<w:pStyle w:val="TOC${level}"/>
|
||
<w:tabs><w:tab w:val="right" w:leader="dot" w:pos="9000"/></w:tabs>
|
||
<w:ind w:left="${indent}"/>
|
||
</w:pPr>
|
||
<w:r><w:t>${xmlEscape(entryTitle)}</w:t></w:r>
|
||
<w:r><w:tab/></w:r>
|
||
<w:r><w:t>${xmlEscape(page)}</w:t></w:r>
|
||
</w:p>`;
|
||
})
|
||
.join("");
|
||
return ImportedXmlComponent.fromXmlString(`<w:sdt xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||
<w:sdtPr><w:alias w:val="目录"/></w:sdtPr>
|
||
<w:sdtContent>
|
||
<w:p>
|
||
<w:r>
|
||
<w:fldChar w:fldCharType="begin" w:dirty="true"/>
|
||
<w:instrText xml:space="preserve"> TOC \\o "1-3" \\h \\z \\u </w:instrText>
|
||
<w:fldChar w:fldCharType="separate"/>
|
||
</w:r>
|
||
</w:p>
|
||
${cached}
|
||
<w:p><w:r><w:fldChar w:fldCharType="end"/></w:r></w:p>
|
||
</w:sdtContent>
|
||
</w:sdt>`).root[0];
|
||
};
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// 内容流:{t:'h1'|'h2'|'h3'|'p'|'b'|'note'|'code'|'tbl'|'cap'|'pagebreak'}
|
||
// ---------------------------------------------------------------------------
|
||
const flow = [];
|
||
const tocEntries = [];
|
||
let estPage = 4;
|
||
const H1 = (x) => {
|
||
tocEntries.push({ title: x, level: 1, page: estPage++ });
|
||
flow.push({ t: "h1", x });
|
||
};
|
||
const H2 = (x) => {
|
||
flow.push({ t: "h2", x });
|
||
};
|
||
const H3 = (x) => flow.push({ t: "h3", x });
|
||
const P = (x) => flow.push({ t: "p", x });
|
||
const B = (x) => flow.push({ t: "b", x });
|
||
const N = (x) => flow.push({ t: "note", x });
|
||
const CODE = (...lines) => flow.push({ t: "code", x: lines });
|
||
const TBL = (cap, headers, rows, widths) =>
|
||
flow.push({ t: "tbl", cap, h: headers, r: rows, w: widths });
|
||
const PAGEBREAK = () => flow.push({ t: "pagebreak" });
|
||
|
||
// ===================== 第一章 =====================
|
||
H1(T`第一章 系统概述`);
|
||
H2(T`1.1 系统定位`);
|
||
P(T`工业智核 APS 计划排产智能体是一套以“自然语言对话 + 可视化排产工作台”双交互范式为核心的高级计划与排产系统。系统左侧为对话通道,右侧为可视化工作台,用户以一句话发起业务动作,右侧视图随即刷新,形成“左说右动”的闭环交互。`);
|
||
P(T`系统核心业务链路为:销售订单 → MRP 分解(自制 / 采购 / 委外)→ 多策略试排(规则引擎 / CP-SAT / 遗传算法 / NSGA-II)→ 方案对比 → P2 确认发布 → MES 下发 → 报工回流。`);
|
||
H2(T`1.2 双排产模型`);
|
||
P(T`系统并行维护两条排产轨道,分别对应不同的生产组织形态,界面对当前所走链路予以明确标识,禁止混谈。`);
|
||
TBL(T`表 1-1 双排产模型对比`,
|
||
[T`轨道`, T`资源模型`, T`适用场景`],
|
||
[
|
||
[T`固定轨`, T`产线选线 + 工位占槽(RuleEngine / CP / GA / HYBRID)`, T`产线固定的常规离散制造`],
|
||
[T`柔性轨`, T`能力池 + 虚拟产线(PoolEngine)`, T`多品种小批量、设备可移动(如线束 / PDU 共线)`],
|
||
],
|
||
[1600, 4200, 3400]);
|
||
H2(T`1.3 安全与治理原则`);
|
||
P(T`系统一切写操作均经门禁确认卡执行:P2 级动作出具单次确认卡,P3 级外部副作用动作执行二次确认;执行前自动建立检查点,全程写入审计哈希链。试排仅产出 DRAFT 草稿版本,系统不自动发布;回滚执行前自动再次存档,构成可恢复的安全网。`);
|
||
|
||
// ===================== 第二章 =====================
|
||
H1(T`第二章 运行环境与系统启动`);
|
||
H2(T`2.1 交付形态`);
|
||
TBL(T`表 2-1 Web 与桌面双形态对比`,
|
||
[T`形态`, T`数据位置`, T`启动方式`, T`账号体系`],
|
||
[
|
||
[T`Web`, T`仓库 server/data/(或 APS_HOME)`, T`npm run dev:web,浏览器访问 http://localhost:5173`, T`JMS 企业账号登录`],
|
||
[T`桌面`, T`C:\\Users\\<用户>\\.aps(与开发环境隔离)`, T`npm run dev:desktop 或安装 release 目录下的 NSIS 安装包`, T`免账号,授权码激活`],
|
||
],
|
||
[1100, 2900, 3100, 2100]);
|
||
H2(T`2.2 依赖安装`);
|
||
P(T`首次部署需完成 Python 与 Node 双侧依赖安装,在仓库根目录依次执行:`);
|
||
CODE(
|
||
T`cd <aps-agent-repo>`,
|
||
T`python -m venv .venv`,
|
||
T`.venv\\Scripts\\activate`,
|
||
T`pip install -r requirements.txt`,
|
||
T`copy .env.example .env`,
|
||
T`npm run bootstrap`
|
||
);
|
||
H2(T`2.3 Web 端登录配置`);
|
||
P(T`Web 端联调用户登录前,须在 .env 文件中配置 JMS 地址与本地会话密钥:`);
|
||
CODE(
|
||
T`APS_AUTH_PROVIDER=jms`,
|
||
T`JMS_AUTH_BASE_URL=https://<your-jms-host>`,
|
||
T`JMS_AUTH_SESSION_SECRET=至少32个随机字符`,
|
||
T`APS_LICENSE_PROVIDER=mock`
|
||
);
|
||
P(T`如需限制为单一企业,可追加配置 JMS_AUTH_TENANT_CODE 与 JMS_AUTH_TENANT_NAME。JMS 当前无自助注册与自助找回密码接口,账号创建与密码重置由所属企业管理员处理。`);
|
||
H2(T`2.4 启动与打包`);
|
||
B(T`开发运行(Web):npm run dev:web,前端 http://localhost:5173,API http://localhost:8000;`);
|
||
B(T`开发运行(桌面):npm run dev:desktop,启动 Electron 窗口;`);
|
||
B(T`仅起后端:npm run dev:server,OpenAPI 文档位于 http://localhost:8000/docs;`);
|
||
B(T`打包 Web:npm run build:web,产物为 apps/web/dist 静态站点;`);
|
||
B(T`打包桌面:npm run build:desktop,产物为 apps/desktop/release 下的安装包。`);
|
||
|
||
// ===================== 第三章 =====================
|
||
H1(T`第三章 登录授权与界面导览`);
|
||
H2(T`3.1 登录与授权`);
|
||
P(T`Web 端:打开页面后选择所属企业(企业列表自 JMS 读取),输入真实账号密码登录。桌面端:输入授权码激活,开发演示授权码为 APS-HOUR-DEMO、APS-DAY-DEMO、APS-WEEK-DEMO、APS-MONTH-DEMO、APS-YEAR-DEMO、APS-PERP-DEMO。`);
|
||
H2(T`3.2 界面布局`);
|
||
P(T`主界面由四区构成:顶部为中文菜单栏(按功能开关过滤);左侧栏承载项目 / 任务与订单、主数据等快捷入口;中部为对话区与工作台;右侧为工具导轨,依次提供多智能体编排、DXF 图纸、柔性工作台、知识库、治理台等工具入口。`);
|
||
P(T`对话流中内嵌方案卡、确认卡、知识引用、报告等富交互块,可直接点按操作。右侧视口包括:固定 / 柔性甘特、负荷、交期、分桶计划、利用率、对比表、KPI 仪表盘、产能池与时间线。`);
|
||
H2(T`3.3 快捷交互`);
|
||
P(T`Ctrl+K 唤起命令面板,可快速跳转任意面板与视图,条目随功能开关配置过滤;当前展开的工具被禁用时面板自动隐藏。`);
|
||
|
||
// ===================== 第四章 =====================
|
||
H1(T`第四章 数据准备`);
|
||
H2(T`4.1 工程目录导入`);
|
||
P(T`于左侧栏选择“新建任务 / 新建项目”,将工程目录指向数据文件夹(内含订单、物料、BOM、工艺、设备、班历等 Excel/CSV 文件),随后在对话框输入“分析一下数据文件”。系统执行项目深度分析,输出订单、物料、工艺、设备、BOM 计数及结构化诊断,并自动导入排产世界。诊断按“有效 / 阻断(blocking)/ 跳过”三档统计,阻断项定位至具体文件与行号。`);
|
||
H2(T`4.2 Excel/CSV 导入管线`);
|
||
P(T`主数据面板与订单面板均设“导入”入口。导入遵循“表头映射 → 预览校验 → P2 确认落库”三段式管线,支持订单、物料、BOM、工艺路线、设备、模具、工序、区域八类对象。`);
|
||
H2(T`4.3 DXF 工程图纸识别`);
|
||
P(T`右侧导轨“图纸”入口支持上传 DXF、填写本地路径或扫描工程目录。系统自动解析标题栏(图号、名称、比例、版本,逐字段标注置信度与来源),生成支持缩放、平移与图层开关的 SVG 预览;物料候选经人工核对后生成 P2 确认卡,批准后方写入主数据。`);
|
||
N(T`图纸未承载的字段(材质、工时、外协属性、BOM 子件数量等)一律显式标记 missing,绝不编造,必须经人工确认。`);
|
||
H2(T`4.4 演示数据装载`);
|
||
P(T`柔性排产演示可装载康尼真实单数据,在仓库根目录执行下列脚本,或以口令“导入完整生产路线”替换演示数据:`);
|
||
CODE(T`.venv\\Scripts\\python.exe scripts\\load_kangni.py`);
|
||
P(T`另提供纯内存、确定性结果的一键全流程模拟脚本(无需启动服务),适用于演示与验收:`);
|
||
CODE(T`.venv\\Scripts\\python.exe scripts\\flex_simulation.py`);
|
||
H2(T`4.5 主数据录入顺序`);
|
||
P(T`主数据之间存在严格依赖关系,录入必须遵循如下顺序:先工厂 / 产线,再物料,再 BOM,再工序,再工艺路线,再产线-产品绑定,最后录入订单。任一环节缺失,后续排产必然失败或结果“找不到依据”。`);
|
||
|
||
// ===================== 第五章 =====================
|
||
H1(T`第五章 主数据维护`);
|
||
P(T`侧栏“主数据”面板按页签组织,全部编辑动作生成 P2 确认卡,经批准后执行;执行前自动建立检查点并写入审计链。主数据修改仅影响其后生成的新排产版本,历史版本不回写。`);
|
||
TBL(T`表 5-1 主数据页签与能力`,
|
||
[T`页签`, T`维护内容`, T`权力等级`],
|
||
[
|
||
[T`资源`, T`工厂→车间→产线→工位→设备资源树;产线编辑 / 停启用(停用产线不进入新排产)`, T`编辑 P2`],
|
||
[T`物料与 BOM`, T`库存 / 在途 / 安全库存 / 前置期;BOM 明细用量与关键料`, T`P2`],
|
||
[T`日历与维保`, T`班次 / 日历只读;维保窗口新增 / 取消,联动设备避让`, T`P2`],
|
||
[T`工艺`, T`工艺步骤工时、外协标记`, T`P2`],
|
||
[T`换型`, T`产品族×产品族换型时间矩阵(口令“查看换型矩阵”)`, T`P2`],
|
||
[T`柔性资源`, T`设备虚拟档案(能力集合、可移动性、移动耗时、当前位置、工装适配)与能力池摘要`, T`只读 + Excel 导入`],
|
||
],
|
||
[1500, 5700, 2000]);
|
||
|
||
// ===================== 第六章 =====================
|
||
H1(T`第六章 订单管理`);
|
||
H2(T`6.1 销售订单`);
|
||
P(T`左侧栏“订单”面板提供订单列表与明细维护,新增、编辑、取消、完成均走 P2 确认卡。状态为 CANCELLED 或 COMPLETED 的订单不进入新的排产版本;仅 APPROVED 状态订单参与排产。`);
|
||
H2(T`6.2 订单池与审核`);
|
||
P(T`订单池以泳道看板组织,支持提交、批准、驳回与批量批准。对应口令:“订单池”“提交审核”“批量批准”。`);
|
||
H2(T`6.3 预测订单`);
|
||
P(T`预测订单台账默认不进入正式排产;可通过“预测纳入试排”执行 what-if 分析,确认后以“预测转正”转为正式 APPROVED 销售订单。相关口令:“查看预测”“新建预测”“预测纳入试排”“预测转正”。`);
|
||
H2(T`6.4 紧急插单`);
|
||
P(T`订单面板设插单向导,亦可使用口令“插单快评”查看影响评估,再以“采用插单”应用。固定轨执行 rush.evaluate / rush.apply 流程,柔性轨走 flex.rush;当局部修复扰动超阈值时自动升级为全量重排并出具 P2 确认卡。`);
|
||
H2(T`6.5 订单追溯`);
|
||
P(T`任一订单支持一键追溯完整单据链:订单本体(客户、交期、产品、数量、状态)→ 主数据依据(BOM、工艺路线、库存)→ MRP 分解结果 → 排产版本与工单 → 缺失项(无 BOM / 无路线 / 未绑定产线 / 库存不足)。柔性轨道另沿 FO→BOM→虚拟产线→工单→设备负荷链路追溯。`);
|
||
|
||
// ===================== 第七章 =====================
|
||
H1(T`第七章 物料需求分解(MRP)`);
|
||
P(T`入口为订单面板“分解建议”“分解此单”,或对话口令“分解订单 SO-001”“执行MRP分解”“生成采购建议”。分解逻辑全部源自主数据,修改主数据即改变分解结果。`);
|
||
TBL(T`表 7-1 MRP 分解三分支`,
|
||
[T`分支`, T`依据`, T`产出`],
|
||
[
|
||
[T`自制`, T`订单明细成品 + 工艺路线`, T`自制建议,交由排产引擎生成生产订单与工单`],
|
||
[T`采购`, T`BOM 净需求 = 毛需求 − 库存 − 在途 − 已下达供应`, T`采购建议 DRAFT:数量与建议下单日(按前置期倒排)`],
|
||
[T`委外`, T`工艺步骤外协标记`, T`委外建议 DRAFT:产品、工序、数量、需求日`],
|
||
],
|
||
[1200, 3800, 4200]);
|
||
B(T`分解为 P1 级动作:仅写建议草稿,幂等替换,可重复执行;`);
|
||
B(T`下达经口令“下达MRP建议单”触发 P2 确认卡,批准后 DRAFT 转 RELEASED,并自动补全预计到料日,供齐套检查引用;`);
|
||
B(T`多层 BOM 完整展开:半成品缺少工艺路线时不伪装为采购建议,缺工艺条目予以明示。`);
|
||
|
||
// ===================== 第八章 =====================
|
||
H1(T`第八章 排产操作`);
|
||
H2(T`8.1 固定轨试排`);
|
||
P(T`口令示例:“试排一版交期优先”“按产能均衡排一下”“换型最小化试排”“战役合并试排”。试排产出 DRAFT 版本与冲突清单,右侧甘特 / 负荷 / 交期视图即时刷新。`);
|
||
B(T`策略模板:DELIVERY_FIRST(交期优先)、FIFO、CAPACITY_BALANCE(产能均衡)、CHANGEOVER_MIN(换型最小化)、COST_FIRST、COMPREHENSIVE、CAMPAIGN(批次合并);`);
|
||
B(T`求解引擎:RULE、CP-SAT(工序级 + Cumulative 聚合容量)、GA、NSGA-II(三目标 Pareto)、HYBRID;`);
|
||
B(T`滚动时域:realtime60m(1 分钟粒度)、short2h、mid2d、long7d 多级窗口。`);
|
||
H2(T`8.2 柔性轨排产`);
|
||
P(T`口令“跑一版柔性排产”或经右侧“柔性工作台”操作。系统按订单工艺路线逐工序自能力池动态取用设备,临时拼装虚拟产线(计入模具换型与设备移动耗时),用完即散、设备归还池中。柔性甘特支持“虚拟线预演”,并提供正排 / 倒排 / 瓶颈锚三模式对比。`);
|
||
TBL(T`表 8-1 柔性排产接口参数(POST /api/flex/schedule)`,
|
||
[T`参数`, T`取值`, T`含义`],
|
||
[
|
||
[T`sortMode`, T`ASC / DESC / BOTTLENECK`, T`正排 / 倒排 / 瓶颈锚(默认瓶颈锚)`],
|
||
[T`orderIds`, T`订单 id 数组`, T`仅排指定订单,缺省排全部`],
|
||
],
|
||
[1800, 3200, 4200]);
|
||
H2(T`8.3 方案对比与采用`);
|
||
P(T`口令“对比几种策略”触发沙盒内多策略并行试排,返回方案卡组(KPI、差异、Pareto 前沿与偏好推荐)。点击“采用此方案”将所选策略落入主干草稿。须注意:对比不等于发布,采用前主干世界保持不变。`);
|
||
H2(T`8.4 发布、存档与回滚`);
|
||
TBL(T`表 8-2 版本生命周期操作`,
|
||
[T`操作`, T`口令`, T`权级`, T`语义`],
|
||
[
|
||
[T`发布`, T`发布这个版本`, T`P2`, T`DRAFT→PUBLISHED;仅完整 DRAFT 可发布,执行前自动建档`],
|
||
[T`存档`, T`存个档`, T`P1`, T`对话上下文与世界状态成对快照`],
|
||
[T`回滚`, T`回滚 / 点时间线检查点`, T`P2`, T`回滚前自动再次存档(安全网)`],
|
||
[T`重置`, T`清空数据重新初始化`, T`P2`, T`重播演示种子(电子装配演示工厂)`],
|
||
],
|
||
[1200, 2700, 1000, 4300]);
|
||
H2(T`8.5 工单拖拽调程`);
|
||
P(T`固定与柔性甘特均支持直接拖动工单:15 分钟吸附、拖动过程实时校验、松手生成 P2 确认卡,批准后形成新版本。`);
|
||
H2(T`8.6 冲突处理`);
|
||
P(T`冲突中心按冲突类型给出修复建议:安全修复为 P1 一键应用;涉及重排的出具 P2 确认卡。硬阻断(缺料、无可用设备等)展示聚合计数、具体对象、证据与修复入口,绝不显示绿色“排产完成”。`);
|
||
|
||
// ===================== 第九章 =====================
|
||
H1(T`第九章 计划层功能`);
|
||
P(T`计划层(Advanced Planning)在排产前提供中长期粗能力评估与决策支持,各项能力及对应口令如下表。`);
|
||
TBL(T`表 9-1 计划层能力一览`,
|
||
[T`能力`, T`口令`, T`功能说明`],
|
||
[
|
||
[T`时间分桶计划`, T`时间分桶 / 混合分桶`, T`近 7 日 / 中 4 周 / 远月混合时段建模`],
|
||
[T`粗能力评估`, T`有限产能粗评 / 无限产能粗评 / 粗能力对比`, T`FINITE / INFINITE 双模式对照`],
|
||
[T`可行性分析`, T`可行性分析 / 能否按期`, T`累计粗能力对交期需求,定位缺口桶与订单 slack`],
|
||
[T`库存投影`, T`库存投影`, T`期初 + 在途 + 毛需求 + 到货的日 / 周库存曲线,断料与安全库存告警`],
|
||
[T`产能平衡`, T`削峰 / 产能平衡`, T`超载桶向空档挪动建议,前后负荷对照`],
|
||
[T`产供决策`, T`产供决策 / 加班建议`, T`残留缺口沿加班→扩线→外协给出结构化建议`],
|
||
[T`交期承诺`, T`对话询价 / 柔性工作台询价`, T`询单插入模拟,输出乐观 / 预计 / 悲观三区间与资源缺口`],
|
||
[T`瓶颈产能`, T`视口“产能池”`, T`日产能 = 瓶颈工序可用工时 ÷ 单件工时;设备停机 / 调区即时重算,含 4h / 1d / 1w 预测与 85 / 95 预警`],
|
||
],
|
||
[1900, 2700, 4600]);
|
||
|
||
// ===================== 第十章 =====================
|
||
H1(T`第十章 动态调度与异常重排`);
|
||
P(T`系统对生产异常实施 L1–L4 分级重排机制,扰动逐级扩大,确认门禁随级别提升。`);
|
||
TBL(T`表 10-1 分级重排机制`,
|
||
[T`级别`, T`典型触发`, T`重排行为`],
|
||
[
|
||
[T`L1`, T`设备故障(优先)`, T`池内备机换机,最小扰动`],
|
||
[T`L2`, T`短窗变化`, T`窗内重排,P2 确认`],
|
||
[T`L3`, T`日级变化`, T`日窗重排,P2 确认`],
|
||
[T`L4`, T`重大异常`, T`全局重排,P2 确认`],
|
||
],
|
||
[1100, 2900, 5200]);
|
||
P(T`典型场景:可移动设备故障一台时,能力池存在冗余,交期几乎不受影响;单一瓶颈设备(如唯一激光焊接机)故障时,系统立即报告“无可用设备”,并提示启用备机、外协或重新议定交期。WMS 缺料事件触发“缺料 → 重排 → 确认 → MES 下发 → 回执”全链路闭环。`);
|
||
|
||
// ===================== 第十一章 =====================
|
||
H1(T`第十一章 结果视图与报表`);
|
||
TBL(T`表 11-1 结果视口一览`,
|
||
[T`视口`, T`内容`],
|
||
[
|
||
[T`甘特`, T`固定 / 柔性双轨,工单拖拽与冲突高亮`],
|
||
[T`负荷 / 利用率`, T`产线与设备负荷、柔性设备 7 日热力、班组并发触顶提示`],
|
||
[T`交期看板`, T`订单准时 / 延误一览`],
|
||
[T`对比表`, T`固定策略与柔性三模式 KPI 矩阵,最优格高亮并标注相对 delta`],
|
||
[T`KPI 仪表盘`, T`利用率 / 准时率 / 在制 / 延误汇总(口令“打开仪表盘”)`],
|
||
[T`产能池`, T`池负载率、瓶颈色标、短中长期预测、设备调配 What-if`],
|
||
[T`时间线`, T`检查点列表,点击即回滚`],
|
||
],
|
||
[2100, 7100]);
|
||
P(T`报表经口令“生成日报”“版本对比”生成,数字取自冻结快照模板,不经大模型改写。审计支持哈希链校验、Merkle 树、断链检测与归档加密导出。`);
|
||
|
||
// ===================== 第十二章 =====================
|
||
H1(T`第十二章 MES 下发与执行闭环`);
|
||
P(T`已发布版本经柔性工作台“MES 执行”或下发口令进入下发流程:P3 二次确认后,Mock MES 幂等下发。报工回流经 mes.report(P1)回报进度与完工,订单整单完工状态联动推进。订单页阶段状态取自持久化的闭环问题与 MES 事实,刷新后不退回静态“待确认”。`);
|
||
|
||
// ===================== 第十三章 =====================
|
||
H1(T`第十三章 多智能体编排`);
|
||
P(T`右侧栏“多智能体编排”面板支持按需组建智能体团队并自动分发执行,操作步骤如下。`);
|
||
B(T`创建智能体:输入名称并选择角色(数据解析 / MRP 分解 / 柔性排产 / 报告等);`);
|
||
B(T`挂载 Goal:填写标题并选用模板(如“解析→分解→下达→排产→报告”五任务模板),看门狗随即开始监控;`);
|
||
B(T`分发执行:看门狗按能力边界将任务分发给空闲智能体,无合适智能体时自动新建;`);
|
||
B(T`消息总线:实时显示任务接力过程,亦可手动向任意智能体发消息,对方回报自身状态与能力边界;`);
|
||
B(T`看门狗机制:RUNNING 超期(默认 45 秒,可一键调至 10 秒用于演示)触发收回重排,重试耗尽升级 ATTENTION;全部任务完成自动结案 ACHIEVED;任务失败诚实落 FAILED 并升级,不伪报成功。`);
|
||
P(T`重复演示前,先在看门狗区执行一次“恢复”(或调用 POST /api/mesh/reset)。`);
|
||
|
||
// ===================== 第十四章 =====================
|
||
H1(T`第十四章 对话口令速查`);
|
||
TBL(T`表 14-1 自然语言口令速查表`,
|
||
[T`类别`, T`口令`],
|
||
[
|
||
[T`数据`, T`分析一下数据文件 / 导入完整生产路线 / 清空数据重新初始化`],
|
||
[T`MRP`, T`执行MRP分解 / 分解订单 SO-001 / 生成采购建议 / 下达MRP建议单`],
|
||
[T`排产`, T`试排一版交期优先 / 按产能均衡排一下 / 换型最小化试排 / 战役合并试排 / 跑一版柔性排产 / 三模式对比`],
|
||
[T`方案`, T`对比几种策略 / 采用此方案 / 发布这个版本`],
|
||
[T`插单`, T`插单快评 / 采用插单`],
|
||
[T`计划层`, T`时间分桶 / 可行性分析 / 能否按期 / 库存投影 / 削峰 / 产供决策 / 加班建议`],
|
||
[T`订单`, T`订单池 / 提交审核 / 批量批准 / 查看预测 / 新建预测 / 预测纳入试排 / 预测转正`],
|
||
[T`主数据`, T`查看换型矩阵 / 提高VIP权重到10`],
|
||
[T`知识`, T`换线有什么规定 / @知识:标题`],
|
||
[T`报告`, T`生成日报 / 版本对比 / 打开仪表盘`],
|
||
[T`存档`, T`存个档 / 回滚`],
|
||
],
|
||
[1500, 7700]);
|
||
N(T`意图管线为“正则快路 → LLM JSON”两级结构;LLM 超时或非法输出自动降级为正则意图,知识未命中时系统明确告知,不编造 SOP。`);
|
||
|
||
// ===================== 第十五章 =====================
|
||
H1(T`第十五章 治理与安全`);
|
||
H2(T`15.1 门禁分级`);
|
||
P(T`门禁按 P0–P3 四级划分:P0 只读;P1 写草稿直通;P2 单次确认卡;P3 外部副作用二次确认(当前用于 MES 下发)。审批角色可按动作分级配置。所有写操作的唯一执行通道为 /api/actions/confirm,不存在绕过确认卡的直接执行接口。`);
|
||
H2(T`15.2 审计与约束`);
|
||
P(T`每次执行均写入审计链,哈希链可校验,支持断链检测(治理台显示断链徽标)、Merkle 树、归档加密与导出。约束配置中心提供 13 类约束目录,可配置启停与硬 / 软属性,硬约束拦截发布。行业规则包支持 SOP 向排产约束的编译,预览后 P2 应用。`);
|
||
H2(T`15.3 功能开关`);
|
||
P(T`配置文件 server/data/features.json 控制 12 个功能键(visual / orders / master / flex / timeline / drawing / mesh / knowledge / skills / gov / regen / settings)的界面可见性。修改文件后刷新浏览器即生效,无需重启;文件缺失或损坏时全部默认开启(fail-open)。功能开关仅隐藏入口,不替代 Harness 门禁。`);
|
||
|
||
// ===================== 第十六章 =====================
|
||
H1(T`第十六章 常见问题与验收`);
|
||
H2(T`16.1 常见问题`);
|
||
TBL(T`表 16-1 常见问题与处置`,
|
||
[T`问题`, T`处置方法`],
|
||
[
|
||
[T`排产失败提示缺失项`, T`按第 4.5 节顺序检查主数据,并以订单“追溯”定位具体缺失`],
|
||
[T`排产被硬阻断(缺料 / 无设备)`, T`查看冲突中心证据:缺料先下达采购建议;无设备查停用、维保与故障状态`],
|
||
[T`试排后想撤回`, T`时间线点击上一检查点回滚(P2),回滚前系统自动再存档`],
|
||
[T`三模式对比结果一致`, T`产能宽裕时三模式趋同,压缩交期制造压力后分化`],
|
||
[T`删除前端“项目”后数据仍在`, T`项目属前端工作区,排产世界在后端,清世界用“清空数据重新初始化”`],
|
||
[T`Web 无法登录`, T`检查 .env 的 JMS 配置与会话密钥;账号问题联系企业管理员`],
|
||
[T`查看接口定义`, T`启动后端后访问 http://localhost:8000/docs`],
|
||
],
|
||
[3100, 6100]);
|
||
H2(T`16.2 验收快速路径`);
|
||
B(T`口令“分析一下数据文件”,工程目录导入成功;`);
|
||
B(T`口令“执行MRP分解”,采购建议出现;口令“下达MRP建议单”,批准后转 RELEASED;`);
|
||
B(T`口令“对比几种策略”,方案卡出现后采用,右侧甘特刷新;`);
|
||
B(T`口令“存个档”,点击检查点回滚并完成 P2 确认;`);
|
||
B(T`口令“跑一版柔性排产”,虚拟产线与工单出现;`);
|
||
B(T`口令“发布这个版本”,P2 批准后版本转 PUBLISHED;`);
|
||
B(T`回归测试:pytest tests/golden -q(当前基线 1071 项通过,1 项既有失败 test_preference_features,与业务无关)。`);
|
||
|
||
// ===================== 文档装配 =====================
|
||
const renderItem = (item) => {
|
||
switch (item.t) {
|
||
case "h1": return [h1(item.x)];
|
||
case "h2": return [h2(item.x)];
|
||
case "h3": return [h3(item.x)];
|
||
case "p": return [body(item.x)];
|
||
case "b": return [bullet(item.x)];
|
||
case "note": return [note(item.x)];
|
||
case "code": return codeBlock(item.x);
|
||
case "tbl": return [caption(item.cap), table3(item.h, item.r, item.w), para(run(""))];
|
||
case "pagebreak": return [new Paragraph({ children: [new PageBreak()] })];
|
||
default: return [];
|
||
}
|
||
};
|
||
|
||
const coverChildren = [
|
||
para(run(T`工业智核 APS 计划排产智能体`, { bold: true, size: 48, color: palette.dark, font: fontHead }), {
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { before: 3600, after: 240 },
|
||
}),
|
||
para(run(T`操 作 手 册`, { bold: true, size: 44, color: palette.dark, font: fontHead }), {
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { after: 1200 },
|
||
}),
|
||
para(run(T`—— 基于系统当前能力的标准作业规程 ——`, { size: 24, color: palette.light }), {
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { after: 2400 },
|
||
}),
|
||
para(run(T`文档版本:V1.0`, { size: 24 }), { alignment: AlignmentType.CENTER, spacing: { after: 160 } }),
|
||
para(run(T`编制日期:2026 年 9 月 1 日`, { size: 24 }), { alignment: AlignmentType.CENTER, spacing: { after: 160 } }),
|
||
para(run(T`适用对象:计划员、实施顾问、演示与验收人员`, { size: 24 }), { alignment: AlignmentType.CENTER }),
|
||
new Paragraph({ children: [new PageBreak()] }),
|
||
];
|
||
|
||
const prefaceChildren = [
|
||
h1(T`前 言`),
|
||
body(T`本手册依据工业智核 APS 计划排产智能体当前代码能力编制,内容对齐产品功能清单(docs/product/features.md)、核心业务旅程(docs/product/journeys.md)及截至 2026 年 8 月 26 日的变更记录(含 round-65 至 round-67、DXF-01 图纸识别、FF-01 功能开关)。`),
|
||
body(T`手册共十六章:第一至三章介绍系统定位、运行环境与界面;第四至八章按业务顺序阐述数据准备、主数据、订单、MRP 与排产操作;第九至十三章阐述计划层、动态调度、结果视图、MES 闭环与多智能体编排;第十四至十六章给出口令速查、治理安全与常见问题处置。`),
|
||
body(T`本手册仅描述当前代码真实路径。凡标注权级(P0–P3)的操作,其执行均受门禁确认卡约束,任何绕过确认的执行通道在系统中不存在。`),
|
||
new Paragraph({ children: [new PageBreak()] }),
|
||
para(run(T`目 录`, { bold: true, size: 32, color: palette.dark, font: fontHead }), {
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { before: 240, after: 240 },
|
||
}),
|
||
note(T`打开文档后,右键目录选择“更新域”即可刷新页码。`),
|
||
toc(tocEntries),
|
||
new Paragraph({ children: [new PageBreak()] }),
|
||
];
|
||
|
||
const bodyChildren = [];
|
||
for (const item of flow) {
|
||
bodyChildren.push(...renderItem(item));
|
||
}
|
||
|
||
const doc = new Document({
|
||
features: { updateFields: true },
|
||
sections: [
|
||
{
|
||
properties: {
|
||
page: {
|
||
size: { width: 11906, height: 16838 },
|
||
margin: { top: 1440, bottom: 1440, left: 1560, right: 1560 },
|
||
},
|
||
},
|
||
headers: {
|
||
default: new Header({
|
||
children: [
|
||
para(run(T`工业智核 APS 计划排产智能体 · 操作手册`, { size: 18, color: palette.light }), {
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { after: 0 },
|
||
}),
|
||
],
|
||
}),
|
||
},
|
||
footers: {
|
||
default: new Footer({
|
||
children: [
|
||
para(new TextRun({ children: [PageNumber.CURRENT], size: 18 }), {
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { after: 0 },
|
||
}),
|
||
],
|
||
}),
|
||
},
|
||
children: [...coverChildren, ...prefaceChildren, ...bodyChildren],
|
||
},
|
||
],
|
||
});
|
||
|
||
const buffer = await Packer.toBuffer(doc);
|
||
fs.writeFileSync(outputPath, buffer);
|