| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- 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()
|