|
|
@@ -0,0 +1,266 @@
|
|
|
+from docx import Document
|
|
|
+from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT
|
|
|
+from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
|
+from docx.oxml import OxmlElement
|
|
|
+from docx.oxml.ns import qn
|
|
|
+from docx.shared import Inches, Pt, RGBColor
|
|
|
+
|
|
|
+
|
|
|
+OUT = "大屏数据来源说明-客户版.docx"
|
|
|
+
|
|
|
+
|
|
|
+def shade(cell, fill):
|
|
|
+ tc_pr = cell._tc.get_or_add_tcPr()
|
|
|
+ shd = tc_pr.find(qn("w:shd"))
|
|
|
+ if shd is None:
|
|
|
+ shd = OxmlElement("w:shd")
|
|
|
+ tc_pr.append(shd)
|
|
|
+ shd.set(qn("w:fill"), fill)
|
|
|
+
|
|
|
+
|
|
|
+def set_east_asia_font(run, font_name="Microsoft YaHei"):
|
|
|
+ run.font.name = "Calibri"
|
|
|
+ r_pr = run._element.get_or_add_rPr()
|
|
|
+ r_fonts = r_pr.rFonts
|
|
|
+ if r_fonts is None:
|
|
|
+ r_fonts = OxmlElement("w:rFonts")
|
|
|
+ r_pr.append(r_fonts)
|
|
|
+ r_fonts.set(qn("w:eastAsia"), font_name)
|
|
|
+
|
|
|
+
|
|
|
+def set_table_geometry(table, widths):
|
|
|
+ table.autofit = False
|
|
|
+ table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
|
|
+ tbl_pr = table._tbl.tblPr
|
|
|
+ tbl_w = tbl_pr.find(qn("w:tblW"))
|
|
|
+ if tbl_w is None:
|
|
|
+ tbl_w = OxmlElement("w:tblW")
|
|
|
+ tbl_pr.append(tbl_w)
|
|
|
+ tbl_w.set(qn("w:w"), str(sum(int(w.inches * 1440) for w in widths)))
|
|
|
+ tbl_w.set(qn("w:type"), "dxa")
|
|
|
+
|
|
|
+ margins = tbl_pr.find(qn("w:tblCellMar"))
|
|
|
+ if margins is None:
|
|
|
+ margins = OxmlElement("w:tblCellMar")
|
|
|
+ tbl_pr.append(margins)
|
|
|
+ for name, value in (("top", 80), ("bottom", 80), ("start", 120), ("end", 120)):
|
|
|
+ node = margins.find(qn(f"w:{name}"))
|
|
|
+ if node is None:
|
|
|
+ node = OxmlElement(f"w:{name}")
|
|
|
+ margins.append(node)
|
|
|
+ node.set(qn("w:w"), str(value))
|
|
|
+ node.set(qn("w:type"), "dxa")
|
|
|
+
|
|
|
+ for row in table.rows:
|
|
|
+ for i, width in enumerate(widths):
|
|
|
+ cell = row.cells[i]
|
|
|
+ cell.width = width
|
|
|
+ cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
|
|
|
+ tc_pr = cell._tc.get_or_add_tcPr()
|
|
|
+ tc_w = tc_pr.find(qn("w:tcW"))
|
|
|
+ if tc_w is None:
|
|
|
+ tc_w = OxmlElement("w:tcW")
|
|
|
+ tc_pr.append(tc_w)
|
|
|
+ tc_w.set(qn("w:w"), str(int(width.inches * 1440)))
|
|
|
+ tc_w.set(qn("w:type"), "dxa")
|
|
|
+
|
|
|
+
|
|
|
+def repeat_header(row):
|
|
|
+ tr_pr = row._tr.get_or_add_trPr()
|
|
|
+ node = OxmlElement("w:tblHeader")
|
|
|
+ node.set(qn("w:val"), "true")
|
|
|
+ tr_pr.append(node)
|
|
|
+
|
|
|
+
|
|
|
+def setup_doc(doc):
|
|
|
+ sec = doc.sections[0]
|
|
|
+ sec.top_margin = Inches(1)
|
|
|
+ sec.bottom_margin = Inches(1)
|
|
|
+ sec.left_margin = Inches(1)
|
|
|
+ sec.right_margin = Inches(1)
|
|
|
+ sec.header_distance = Inches(0.492)
|
|
|
+ sec.footer_distance = Inches(0.492)
|
|
|
+
|
|
|
+ normal = doc.styles["Normal"]
|
|
|
+ normal.font.name = "Calibri"
|
|
|
+ normal._element.rPr.rFonts.set(qn("w:eastAsia"), "Microsoft YaHei")
|
|
|
+ normal.font.size = Pt(11)
|
|
|
+ normal.paragraph_format.space_after = Pt(6)
|
|
|
+ normal.paragraph_format.line_spacing = 1.10
|
|
|
+
|
|
|
+ for name, size, color, before, after in [
|
|
|
+ ("Heading 1", 16, "2E74B5", 16, 8),
|
|
|
+ ("Heading 2", 13, "2E74B5", 12, 6),
|
|
|
+ ]:
|
|
|
+ style = doc.styles[name]
|
|
|
+ style.font.name = "Calibri"
|
|
|
+ style._element.rPr.rFonts.set(qn("w:eastAsia"), "Microsoft YaHei")
|
|
|
+ style.font.size = Pt(size)
|
|
|
+ style.font.bold = True
|
|
|
+ style.font.color.rgb = RGBColor.from_string(color)
|
|
|
+ style.paragraph_format.space_before = Pt(before)
|
|
|
+ style.paragraph_format.space_after = Pt(after)
|
|
|
+
|
|
|
+
|
|
|
+def add_title(doc):
|
|
|
+ p = doc.add_paragraph()
|
|
|
+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
|
+ r = p.add_run("MES 大屏数据来源说明")
|
|
|
+ r.bold = True
|
|
|
+ r.font.size = Pt(22)
|
|
|
+ r.font.color.rgb = RGBColor.from_string("0B2545")
|
|
|
+ set_east_asia_font(r)
|
|
|
+
|
|
|
+ sub = doc.add_paragraph()
|
|
|
+ sub.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
|
+ r2 = sub.add_run("客户版:按大屏页面说明每项数据来自哪个业务模块")
|
|
|
+ r2.font.size = Pt(10)
|
|
|
+ r2.font.color.rgb = RGBColor.from_string("5B677A")
|
|
|
+ set_east_asia_font(r2)
|
|
|
+
|
|
|
+
|
|
|
+def add_note(doc, text):
|
|
|
+ table = doc.add_table(rows=1, cols=1)
|
|
|
+ table.style = "Table Grid"
|
|
|
+ cell = table.rows[0].cells[0]
|
|
|
+ cell.text = text
|
|
|
+ shade(cell, "F4F6F9")
|
|
|
+ set_table_geometry(table, [Inches(6.5)])
|
|
|
+ doc.add_paragraph()
|
|
|
+
|
|
|
+
|
|
|
+def add_table(doc, rows):
|
|
|
+ table = doc.add_table(rows=1, cols=3)
|
|
|
+ table.style = "Table Grid"
|
|
|
+ headers = ["大屏展示内容", "数据来源", "统计说明"]
|
|
|
+ for i, header in enumerate(headers):
|
|
|
+ cell = table.rows[0].cells[i]
|
|
|
+ cell.text = header
|
|
|
+ shade(cell, "F2F4F7")
|
|
|
+ for p in cell.paragraphs:
|
|
|
+ for run in p.runs:
|
|
|
+ run.bold = True
|
|
|
+ for item, source, rule in rows:
|
|
|
+ cells = table.add_row().cells
|
|
|
+ cells[0].text = item
|
|
|
+ cells[1].text = source
|
|
|
+ cells[2].text = rule
|
|
|
+ repeat_header(table.rows[0])
|
|
|
+ set_table_geometry(table, [Inches(1.75), Inches(2.15), Inches(2.60)])
|
|
|
+ doc.add_paragraph()
|
|
|
+
|
|
|
+
|
|
|
+def bullet(doc, text):
|
|
|
+ p = doc.add_paragraph(style="List Bullet")
|
|
|
+ p.paragraph_format.left_indent = Inches(0.5)
|
|
|
+ p.paragraph_format.first_line_indent = Inches(-0.25)
|
|
|
+ p.paragraph_format.space_after = Pt(8)
|
|
|
+ p.paragraph_format.line_spacing = 1.167
|
|
|
+ p.add_run(text)
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ doc = Document()
|
|
|
+ setup_doc(doc)
|
|
|
+ add_title(doc)
|
|
|
+ add_note(doc, "说明:本文档只说明大屏上各类数据从系统哪个业务模块取得,以及目前页面采用的大致统计口径,不涉及代码、接口和数据库字段。")
|
|
|
+
|
|
|
+ doc.add_heading("一、MES 综合数据大屏", level=1)
|
|
|
+ add_table(doc, [
|
|
|
+ ("人员数量", "用户管理 / 员工账号信息", "统计系统中已维护的员工用户数量。"),
|
|
|
+ ("设备数量", "设备管理", "统计系统中已维护的设备总数。"),
|
|
|
+ ("计划产量", "系统配置的大屏计划值", "读取系统配置中的计划产量;部分工位计划会优先读取大屏计划管理中的配置。"),
|
|
|
+ ("实际产量", "产品生产记录", "统计系统中已生成的生产产品数量。页面部分位置展示累计产量,部分趋势图按日期统计。"),
|
|
|
+ ("今日 / 近七日产量", "产品生产记录", "按生产记录的生成时间,分别统计当天、近七天或每小时产量。"),
|
|
|
+ ("小时产量", "产品生产记录", "按小时统计当天产量;综合图表中主要展示 8 点到 18 点的小时产量。"),
|
|
|
+ ("返修数量", "返修管理", "按当天返修记录统计,部分地方按产线和工件编号去重后计算。"),
|
|
|
+ ("报废数量", "报废管理", "统计系统中的报废记录数量。"),
|
|
|
+ ("产线不良率报警", "生产记录 + 返修记录", "按产线比较当天生产数量和返修数量,超过设定比例后在大屏显示报警。"),
|
|
|
+ ("工具剩余寿命", "工具寿命管理", "读取正在使用的工具及其当前剩余寿命。"),
|
|
|
+ ("出库数量", "发运 / 出库记录", "按出库记录中的数量汇总,展示今日、本月或近七日出库情况。"),
|
|
|
+ ("当前工位工件", "工位在制信息", "展示当前各工位正在加工或流转的工件编号。"),
|
|
|
+ ("不良问题统计", "返修问题清单", "按返修问题类型汇总数量,用于显示问题类型分布。"),
|
|
|
+ ("设备报警排行", "设备报警记录", "按报警类型汇总数量,展示报警较多的类型。"),
|
|
|
+ ("设备故障率趋势", "设备管理 + 设备报警记录", "按天统计发生过报警的设备数量,再与设备总数比较得到故障率。"),
|
|
|
+ ("NG 超时提醒", "生产过站 / 检测记录", "查找长时间未处理的 NG 记录,在大屏上做提醒。"),
|
|
|
+ ("工位产能列表", "工艺工位 + 生产过站记录 + 大屏计划", "按工位统计今日完成数量、近 12 小时完成数量,并展示对应计划产能。"),
|
|
|
+ ])
|
|
|
+
|
|
|
+ doc.add_heading("二、小时产量看板", level=1)
|
|
|
+ add_table(doc, [
|
|
|
+ ("HEVXT 上线数量", "HEVXT 产线生产过站记录", "统计 HEVXT 产线当天每小时上线工位的记录数量。"),
|
|
|
+ ("HEVXT 下线数量", "HEVXT 产线生产过站记录", "统计 HEVXT 产线当天每小时下线工位的记录数量。"),
|
|
|
+ ("B13XT 上线数量", "B13XT 产线生产过站记录", "统计 B13XT 产线当天每小时上线工位的记录数量。"),
|
|
|
+ ("B13XT 下线数量", "B13XT 产线生产过站记录", "统计 B13XT 产线当天每小时下线工位的记录数量。"),
|
|
|
+ ])
|
|
|
+
|
|
|
+ doc.add_heading("三、质量管理大屏", level=1)
|
|
|
+ add_table(doc, [
|
|
|
+ ("今日检查数量", "产品检验记录", "统计当天产生的产品检验记录数量。"),
|
|
|
+ ("今日首检数量", "首件检验记录", "统计当天产生的首件检验记录数量。"),
|
|
|
+ ("NG 数量", "NG 记录", "统计当天或近七天的 NG 记录数量。"),
|
|
|
+ ("返修数量", "返修管理", "统计当天或近七天的返修记录数量。"),
|
|
|
+ ("报废数量", "报废管理", "统计当天或近七天的报废记录数量。"),
|
|
|
+ ("待返修数量", "返修管理", "统计当前仍处于待处理状态的返修记录。"),
|
|
|
+ ("质量趋势", "检验、NG、返修、报废记录", "按最近七天分别汇总检查、NG、返修和报废数量。"),
|
|
|
+ ("检查状态分布", "产品检验记录", "按检验状态汇总数量。"),
|
|
|
+ ("检查类型分布", "产品检验记录", "按检验类型汇总数量。"),
|
|
|
+ ("首检状态分布", "首件检验记录", "按首件检验状态汇总数量。"),
|
|
|
+ ("NG 类型分布", "NG 记录", "按 NG 类型汇总数量。"),
|
|
|
+ ("返修状态 / 结果分布", "返修管理", "按返修状态和返修结果汇总数量。"),
|
|
|
+ ("报废原因排行", "报废管理", "按报废原因汇总,展示数量较多的原因。"),
|
|
|
+ ("责任工位排行", "检验、NG、返修记录", "按相关工位汇总质量问题数量。"),
|
|
|
+ ("最新质量问题", "检验、NG、返修、报废记录", "展示最近产生的质量异常或处理记录。"),
|
|
|
+ ])
|
|
|
+
|
|
|
+ doc.add_heading("四、物料管理看板", level=1)
|
|
|
+ add_table(doc, [
|
|
|
+ ("物料总数", "物料基础资料", "统计系统中已维护的物料数量。"),
|
|
|
+ ("库存批次数", "库存台账", "统计当前仍有库存数量的批次。"),
|
|
|
+ ("库存总量", "库存台账", "汇总当前正库存数量。"),
|
|
|
+ ("今日入库数量", "入库记录", "汇总当天入库数量。"),
|
|
|
+ ("今日出库数量", "出库记录", "汇总当天出库数量。"),
|
|
|
+ ("今日报检数量", "物料报检记录", "统计当天产生的报检记录数量。"),
|
|
|
+ ("待处理报检", "物料报检记录", "统计当前仍未完成处理的报检记录。"),
|
|
|
+ ("今日异常数量", "不良品、退货、报废记录", "汇总当天物料异常相关记录数量。"),
|
|
|
+ ("效期预警", "库存台账", "统计未来 30 天内即将到期的库存批次。"),
|
|
|
+ ("物料趋势", "入库、出库、报检和异常记录", "按最近七天汇总业务发生数量。"),
|
|
|
+ ("报检 / 检查 / 不良状态", "物料报检、检查、不良品记录", "按状态汇总数量,用于饼图展示。"),
|
|
|
+ ("入库类型分布", "入库单据", "按入库类型汇总数量。"),
|
|
|
+ ("领料状态分布", "领料单据", "按领料状态汇总数量。"),
|
|
|
+ ("库位库存排行", "库存台账", "按库位汇总库存数量,展示库存较多的库位。"),
|
|
|
+ ("物料库存排行", "库存台账", "按物料汇总库存数量,展示库存较多的物料。"),
|
|
|
+ ("库存明细 / 到期清单", "库存台账", "展示当前库存和即将到期的重点批次。"),
|
|
|
+ ("最新业务动态", "报检、检查、入库、出库及异常记录", "展示最近发生的物料业务记录。"),
|
|
|
+ ])
|
|
|
+
|
|
|
+ doc.add_heading("五、设备 OEE 大屏", level=1)
|
|
|
+ add_table(doc, [
|
|
|
+ ("设备总数", "设备管理", "统计系统中已维护的设备数量。"),
|
|
|
+ ("运行 / 故障 / 维修 / 报警 / 离线数量", "设备实时状态记录 + 设备管理", "读取每台设备最新状态后按状态分类汇总。"),
|
|
|
+ ("设备开动率", "设备状态记录", "按当天设备处于运行状态的时长计算,运行时长占当天已过时间的比例。"),
|
|
|
+ ("平均开动率", "设备状态记录", "把所有设备当天运行时长汇总后,计算整体平均开动率。"),
|
|
|
+ ("设备卡片", "设备管理 + 设备状态记录", "展示设备名称、编号、所属产线、当前状态、最近上报时间和开动率。"),
|
|
|
+ ("开动率排行", "设备状态记录", "按设备当天开动率排序,展示开动率较高的设备。"),
|
|
|
+ ])
|
|
|
+
|
|
|
+ add_note(doc, "特别说明:当前设备 OEE 大屏展示的是“设备开动率”,不是完整 OEE。完整 OEE 通常还需要计划运行时间、理想节拍、总产量、良品数量等数据。")
|
|
|
+
|
|
|
+ doc.add_heading("六、客户确认建议", level=1)
|
|
|
+ for text in [
|
|
|
+ "确认综合大屏顶部统计希望展示“今日数据”“本月数据”还是“历史累计数据”。",
|
|
|
+ "确认生产日期口径使用自然日 00:00-24:00,还是生产班次日 08:00-次日 08:00。",
|
|
|
+ "确认质量大屏中良品、不良是否必须全部来自真实检验数据。",
|
|
|
+ "确认设备 OEE 是否只展示开动率,还是后续需要扩展为完整 OEE。",
|
|
|
+ "确认报警、排行类数据是否需要限定为今日、近七天或本月,避免历史累计影响展示判断。",
|
|
|
+ ]:
|
|
|
+ bullet(doc, text)
|
|
|
+
|
|
|
+ footer = doc.sections[0].footer.paragraphs[0]
|
|
|
+ footer.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
|
|
+ footer.add_run("MES 大屏数据来源说明")
|
|
|
+ doc.save(OUT)
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|