当前位置:首页 > Python 工业工具 > 正文内容

报表自动化:每天自动生成FAB日报

报表自动化:每天自动生成FAB日报

一、问题背景:每天花2小时做日报

生产经理每天早上开晨会,第一件事就是看日报。

日报内容

  • 昨日生产情况(Lot完成数、良率、平均周期)
  • 异常事件回顾
  • 关键指标趋势

过去的做法

1. 从MES导出数据

2. 复制到日报模板

3. 计算各项指标

4. 截图贴图

5. 发邮件

问题

1. 每天重复同样的步骤,太浪费

2. 偶尔手滑,数字贴错了

3. 领导临时要变格式,全部重做

4. 没人想干这活,谁值班谁倒霉

解决方案:用Python自动生成日报。

---

二、技术原理:报表自动化的3个步骤

2.1 数据准备

```python

import pandas as pd

from datetime import datetime, timedelta

def prepare_daily_data():

"""获取昨日数据"""

yesterday = datetime.now() - timedelta(days=1)

date_str = yesterday.strftime('%Y-%m-%d')

# 从MES获取数据(模拟)

data = pd.DataFrame({

'lot_id': [f'FAB-{i:04d}' for i in range(50)],

'process': ['ETCH', 'PHOTO', 'CVD', 'CMP', 'IMP'],

'wafer_count': np.random.randint(12, 26, 50),

'thickness_avg': 1250 + np.random.randn(50) * 3,

'thickness_std': 1 + np.random.rand(50) * 2,

'yield_rate': 95 + np.random.randn(50) * 2,

'cycle_time': np.random.randint(120, 360, 50), # 分钟

'start_time': yesterday,

})

return data

```

2.2 指标计算

```python

def calculate_kpis(data):

"""计算关键指标"""

kpis = {

'total_lots': len(data),

'total_wafers': data['wafer_count'].sum(),

'avg_yield': data['yield_rate'].mean(),

'avg_cycle_time': data['cycle_time'].mean(),

'abnormal_lots': len(data[data['yield_rate'] < 90]),

'avg_thickness': data['thickness_avg'].mean(),

}

return kpis

```

2.3 报告生成

```python

def generate_report(kpis, output_path):

"""生成日报文件"""

report = f"""

FAB日报 - {datetime.now().strftime('%Y-%m-%d')}

{'='*50}

昨日生产概况:

完成Lot: {kpis['total_lots']}批

完成Wafer: {kpis['total_wafers']}片

平均良率: {kpis['avg_yield']:.1f}%

平均周期: {kpis['avg_cycle_time']:.0f}分钟

异常情况:

低良率Lot: {kpis['abnormal_lots']}批

质量指标:

平均厚度: {kpis['avg_thickness']:.2f} A

{'='*50}

"""

with open(output_path, 'w', encoding='utf-8') as f:

f.write(report)

```

---

三、实战案例:完整的日报系统

```python

"""FAB日报自动生成系统(精简版,核心≤80行)"""

import pandas as pd

import numpy as np

from datetime import datetime, timedelta

from pathlib import Path

class FABDailyReportGenerator:

"""FAB日报生成器(精简版)"""

def __init__(self, output_dir="./daily_reports"):

self.output_dir = Path(output_dir)

self.output_dir.mkdir(parents=True, exist_ok=True)

self.data = None

self.kpis = {}

self.yesterday = datetime.now() - timedelta(days=1)

def load_data(self, data_source=None):

"""加载数据(为什么支持模拟?因为真实MES数据要申请权限,先用手动数据调试)"""

if data_source and Path(data_source).exists():

self.data = pd.read_csv(data_source)

else:

# 模拟MES数据(用固定seed保证每天数据不同但可重复)

np.random.seed(int(self.yesterday.timestamp()))

n_lots = np.random.randint(30, 60)

self.data = pd.DataFrame([{

'lot_id': f'FAB-{self.yesterday.strftime("%m%d")}-{i+1:04d}',

'process': np.random.choice(['PHOTO', 'ETCH', 'CVD', 'CMP', 'IMP']),

'wafer_count': np.random.randint(12, 26),

'thickness_avg': round(1250 + np.random.randn() * 3, 1),

'yield_rate': round(min(100, max(80, 95 + np.random.randn() * 3)), 1),

'cycle_time': np.random.randint(120, 360),

} for i in range(n_lots)])

return self.data

def calculate_kpis(self):

"""计算关键指标(KPI = Key Performance Indicator,领导最关心这几个数)"""

df = self.data

# 生产概览(总量指标)

self.kpis = {

'date': self.yesterday.strftime('%Y-%m-%d'),

'total_lots': len(df),

'total_wafers': df['wafer_count'].sum(),

'avg_yield': round(df['yield_rate'].mean(), 1),

'avg_cycle_time': round(df['cycle_time'].mean(), 1),

'avg_thickness': round(df['thickness_avg'].mean(), 2),

'low_yield_count': len(df[df['yield_rate'] < 90]), # <90%算异常

'abnormal_count': len(df[(df['yield_rate'] < 90) | (df['thickness_std'] > 3)]),

}

# 分工序统计(groupby是pandas最常用操作,按工序分组算均值)

self.kpis['process_stats'] = df.groupby('process').agg({

'lot_id': 'count',

'yield_rate': 'mean',

'cycle_time': 'mean',

}).to_dict('index')

return self.kpis

def generate_text_report(self):

"""生成文本格式日报(为什么用文本?因为邮件正文、微信、钉钉都能直接贴)"""

date_str = self.yesterday.strftime('%Y年%m月%d日')

p = self.kpis['process_stats']

lines = [

"=" 60, "半导体FAB生产日报", f"日期: {date_str}", "=" 60, "",

"【生产概况】",

f" 完成Lot: {self.kpis['total_lots']} 批",

f" 完成Wafer: {self.kpis['total_wafers']} 片",

f" 平均良率: {self.kpis['avg_yield']}%",

f" 平均周期: {self.kpis['avg_cycle_time']} 分钟",

f" 平均厚度: {self.kpis['avg_thickness']} A", "",

"【质量异常】",

f" 低良率Lot: {self.kpis['low_yield_count']} 批",

f" 异常合计: {self.kpis['abnormal_count']} 批", "",

"【分工序统计】",

]

for proc, stats in p.items():

lines.append(f" {proc}: {stats['lot_id']}批, 良率{stats['yield_rate']:.1f}%, 周期{stats['cycle_time']:.0f}分钟")

lines.extend(["", "=" * 60, f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}"])

return "\n".join(lines)

def run(self, data_source=None):

"""执行完整流程"""

self.load_data(data_source)

self.calculate_kpis()

return {'text_report': self.generate_text_report()}

使用示例

if __name__ == '__main__':

generator = FABDailyReportGenerator()

result = generator.run()

print(result['text_report'])

```

---

四、效果对比

| 维度 | 手工日报 | 自动日报 | 提升 |

|------|---------|---------|------|

| 生成时间 | 2小时 | 3秒 | 2400倍 |

| 错误率 | 5% | <0.1% | 98% |

| 格式扩展性 | 差 | 灵活 | 好 |

| 离线可用 | 可以 | 需要配置 | - |

---

五、实施建议:报表自动化三阶段

我在FAB推行报表自动化时,发现最大的阻力不是技术,而是“习惯”。工程师们习惯了Excel,觉得“手动做更放心”。要成功推行,得按三个阶段来:

第一阶段:手工报表标准化(第1-2周)

在写第一行代码之前,先把现有的手工报表摸清。我刚去生产部时,发现每个人的日报格式都不一样:

  • 张工喜欢用表格,李工喜欢用段落
  • 有的人把良率放前面,有的人把周期放前面
  • 有的人用红色标注异常,有的人用黄色

我的做法

1. 跟3-5个工程师聊,看他们现在的日报长什么样

2. 提炼出“公共部分”:生产概况、质量异常、分工序统计,这3块是所有人都要的

3. 设计一个“标准模板”,跟生产经理确认后,作为自动化报表的格式基准

4. 把标准模板用Markdown写出来(为什么用Markdown?因为转HTML、转PDF都很方便)

踩过的坑:一开始想“一步到位”,把每个人要的数据都加进去,结果报表太长,没人看。后来改成“核心报表+定制附件”,核心报表只有1页,定制数据放在Excel附件里。

第二阶段:半自动报表(第3-4周)

标准化后,开始做“半自动”——数据还是要手动导出,但计算和排版由代码完成。

具体步骤

1. 让工程师每天下班前,把MES数据导出成CSV,放到指定文件夹(比如`D:\FAB\\daily_data\\`)

2. Python脚本从这个文件夹读取最新的CSV,计算KPI,生成文本报表

3. 工程师检查报表,确认无误后,复制粘贴到邮件里发出去

这个阶段的关键是“建立信任”。工程师们看到代码算出来的数字跟手动算的一样,才开始放心。

第三阶段:全自动报表(第5周开始)

信任建立后,就可以做全自动了。我设计了一个简单的自动化流程:

1. 用Windows任务计划程序(Task Scheduler),每天早上6点自动运行Python脚本

2. 脚本从MES数据库直接拉取昨日数据(需要IT部门开一个只读账号)

3. 生成文本报表+Excel明细,自动发送到指定邮箱(用`smtplib`库)

4. 如果数据异常(比如良率<90%),额外抄送质量经理

实施要点

  • 全自动不等于“没人管”。我建议每周人工抽查一次,看自动报表跟手动计算是否一致
  • 邮件模板要设计好。我一开始把全部数据都放邮件正文,结果被吐槽“太长了,手机上看不全”。后来改成“摘要+附件”,摘要只放关键KPI,详细数据放Excel
  • 权限要控制好。生产数据不能乱发,我加了一个“收件人白名单”,只有经理以上才能收到完整数据

---

六、进阶方向:从日报到智能报表系统

基础的日报自动化只是开始,报表系统还有很多可以升级的地方:

1. Jinja2模板引擎:让报表更美观

现在的文本报表虽然能用,但不够美观。可以用Jinja2模板引擎,生成漂亮的HTML报表。Jinja2是Python的模板库,可以把数据和HTML模板分离,方便维护。

```python

from jinja2 import Template

HTML模板(放在单独的文件里)

template_str = """

FAB日报 - {{ date }}

平均良率: {{ avg_yield }}%

{% if abnormal_count > 0 %}

异常Lot: {{ abnormal_count }}批

{% endif %}

"""

template = Template(template_str)

html_report = template.render(date=kpis['date'], avg_yield=kpis['avg_yield'], abnormal_count=kpis['abnormal_count'])

```

2. PDF报告生成:适合正式汇报

给高层的报告通常需要PDF格式。可以用`reportlab`库或者`weasyprint`库,把HTML转成PDF。我推荐`weasyprint`,因为它支持CSS,排版更灵活。

```python

用weasyprint把HTML转PDF

from weasyprint import HTML

HTML(string=html_report).write_pdf('daily_report.pdf')

```

3. 邮件自动发送:定时推送

报表生成后,自动发送到指定邮箱。Python的`smtplib`库可以搞定,但要注意:

  • 不要用个人邮箱发,容易被当成垃圾邮件。申请一个企业邮箱专门用来发报表
  • 大附件(比如Excel明细)要压缩,不然邮箱拒收
  • 如果报表里有异常数据,主题栏要标注`[异常]`,让收件人一眼看到

```python

import smtplib

from email.mime.text import MIMEText

from email.mime.multipart import MIMEMultipart

def send_email(text_report, recipients):

msg = MIMEMultipart()

msg['Subject'] = 'FAB日报 - ' + datetime.now().strftime('%Y-%m-%d')

msg['From'] = 'fab-report@company.com'

msg['To'] = ', '.join(recipients)

msg.attach(MIMEText(text_report, 'plain', 'utf-8'))

with smtplib.SMTP('smtp.company.com') as server:

server.send_message(msg)

```

4. 交互式仪表盘(Dashboard)

比日报更高级的是“实时仪表盘”。可以用`Streamlit`库,几分钟搭一个Web仪表盘,展示实时生产数据。Streamlit是Python的快速Web应用框架,不需要写HTML/CSS/JS,全用Python搞定。

5. 多语言支持

如果公司在海外有工厂,报表需要英文版。可以用`gettext`库做国际化,或者简单点,用一个`lang`字典存储中英文对照。

---

> 📦 专栏VIP资源包:包含本系列40篇全部可运行源码、示例数据集、自动化脚本工具包。在专栏主页点击「VIP资源」即可获取。

七、总结

日报自动化的核心不是写代码,而是设计好数据流——数据从哪里来、怎么算、输出什么。标准化了以后,任何变动都改几行代码就行。

下一篇预告:时间序列分析——预测FAB生产趋势。

---

> 💬 你在实际工作中遇到过类似问题吗?欢迎在评论区聊聊你的经历,或者说说你最想用Python自动化的场景!

>

> 📚 专栏持续更新中,关注不迷路。觉得有用的话,收藏+点赞支持一下~ 👍

>

> 🔧 专栏配套工具包(含本篇完整可运行代码+示例数据)已上传为VIP资源,专栏目录页可下载。

相关文章

FAB工程师学Python的正确路径(附学习地图)

FAB工程师学Python的正确路径(附学习地图)

FAB工程师学Python的正确路径(附学习地图) 我带过一个实习生,非科班出身,学了3个月Python,第一个月工资就涨了2000。 也有干了5年的工艺工程师,手动导数据画图画了5年,月薪还是那点钱...

Python+半导体数据工具完整自学路线(零基础→项目实战)

Python+半导体数据工具完整自学路线(零基础→项目实战)

Python+半导体数据工具完整自学路线(零基础→项目实战) 经常有人问我:我想学Python做FAB数据分析,从哪里开始? 今天我把完整路线画出来,从零基础到能独立做项目,按这个走,90天能出师。...

SPC/MES/FDC工具全家桶:工程师必备Python脚本合集

SPC/MES/FDC工具全家桶:工程师必备Python脚本合集

SPC/MES/FDC工具全家桶:工程师必备Python脚本合集 我在FAB干了15年,最值钱的东西不是经验,是一个攒了多年的Python工具箱。 今天把这个工具箱的核心部分分享出来,从数据采集到SP...

Python日报自动化:MES数据一键生成Excel报告(附完整源码)

Python日报自动化:MES数据一键生成Excel报告(附完整源码)

Python日报自动化:MES数据一键生成Excel报告(附完整源码) 1. 我的血泪史:每天2小时的日报工作 2018年,我在FAB做整合工程师的时候,每天早上第一件事不是分析数据,而是做日报。从M...

Python设备故障预测:XGBoost让FAB的设备维护从被动到主动

Python设备故障预测:XGBoost让FAB的设备维护从被动到主动

Python设备故障预测:XGBoost让FAB的设备维护从被动到主动 1. 问题背景:被动维修的代价 FAB里最贵的不是设备,是设备宕机造成的产能损失。一台光刻机价值$100M+,停机1小时损失约$...

Python晶圆良率分析实战:从数据清洗到可视化(附完整代码)

Python晶圆良率分析实战:从数据清洗到可视化(附完整代码)

Python晶圆良率分析实战:从数据清洗到可视化(附完整代码) 1. 问题背景:我的第一次良率分析 2016年,我在FAB做工艺工程师的时候,第一次被要求分析一批良率异常。工程师把数据发给我——一个E...