aps-agent/server/state/seed.py

474 lines
35 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ============================================================
# 种子数据工厂(moduleId: state-seed, 可重生 ✅)
# 从 legacy aps-frontend/js/data.js 的 seed() 按行移植:
# - 数据键名保持 camelCase 与 JS 版一致(前端/存储直接复用)
# - 固定日期改为"相对今天"的偏移(黄金测试与任意运行日都稳定)
# - Math.random 改为固定种子 Random(42)(可复现,§9.5.2 deterministic)
# ============================================================
from __future__ import annotations # 前向类型引用
import random # 标准随机库(用固定种子保证确定性)
from typing import Any # 类型标注
from server.timeutil import add_minutes, fmt_date, fmt_dt, today0 # 日期工具(与 data.js $utils 对齐)
def seed_world() -> dict[str, Any]:
"""生成一套自洽的演示工厂数据(权力等级 P0:纯构造,不落盘)。"""
rng = random.Random(42) # 固定种子:设备参数可复现
base = today0() # 相对基准:今天零点
data: dict[str, Any] = { # 世界状态骨架(键集与 data.js 一致)
"factories": [], "workshops": [], "lines": [], "workstations": [],
"operations": [], "routings": [], "routingSteps": [], "materials": [],
"boms": [], "bomItems": [], "lineProducts": [], "workstationOperations": [],
"equipment": [], "teams": [], "shifts": [], "shiftCalendar": [],
"maintenance": [], "salesOrders": [], "productionOrders": [], "workOrders": [],
"purchaseOrders": [], "outsourceOrders": [], # MRP 建议表(订单分解产物,DRAFT 建议单)
"forecastOrders": [], # OR-05 预测 / 长周期订单
"changeoverMatrix": [], # MD-06 产品族×产品族换型矩阵
"scheduleVersions": [], "conflicts": [], "logs": [], "auditEvents": [],
# 排产参数(§9.8 业务参数层的 M1 形态)
"scheduleParams": {
"defaultEngine": "RULE", # M1 仅 RULE 真实现
"planningHorizonDays": 14, # 计划展望期
"timeGranularityMinutes": 15, # 排产时间粒度
"deliveryBufferRatio": 0.95, # 交期缓冲比
"weights": {"tardiness": 0.4, "cost": 0.3, "utilization": 0.2, "balance": 0.1}, # 目标权重
"customerLevelWeights": {"VIP": 3.0, "A": 2.0, "B": 1.0, "C": 1.0}, # OR-02 客户等级排序权重
"freezeWindowHours": 24, # 冻结窗口
},
# SC-04 约束剖面(覆盖写在 constraints 子表;缺省走注册表默认)
"constraintProfile": {
"profileId": "default", "name": "默认约束剖面", "constraints": {},
},
}
# ---- 工厂与车间 ----
data["factories"].append({"id": 1, "code": "F001", "name": "演示工厂", "timezone": "Asia/Shanghai", "address": "演示园区", "status": "ACTIVE"}) # 单工厂(示例数据,非真实客户)
data["workshops"].append({"id": 1, "factoryId": 1, "code": "WS01", "name": "一车间", "status": "ACTIVE"}) # 车间 1
data["workshops"].append({"id": 2, "factoryId": 1, "code": "WS02", "name": "二车间", "status": "ACTIVE"}) # 车间 2
# ---- 产线(3 条,含效率因子与替代产线) ----
data["lines"].append({"id": 1, "workshopId": 1, "code": "L001", "name": "装配产线A", "capacityPerDay": 1000, "taktTime": 0.5, "efficiencyFactor": 1.0, "status": "ACTIVE", "alternativeLineIds": [3]})
data["lines"].append({"id": 2, "workshopId": 1, "code": "L002", "name": "装配产线B", "capacityPerDay": 800, "taktTime": 0.6, "efficiencyFactor": 0.95, "status": "ACTIVE", "alternativeLineIds": [1]})
data["lines"].append({"id": 3, "workshopId": 2, "code": "L003", "name": "装配产线C", "capacityPerDay": 600, "taktTime": 0.8, "efficiencyFactor": 0.9, "status": "ACTIVE", "alternativeLineIds": [1]})
# ---- 工位(13 个:产线, 编码, 名称, 顺序号) ----
ws_rows = [
(1, "WS001", "SMT-01", 1), (1, "WS002", "DIP-01", 2), (1, "WS003", "ASSY-01", 3), (1, "WS004", "TEST-01", 4), (1, "WS005", "PKG-01", 5),
(2, "WS006", "SMT-02", 1), (2, "WS007", "ASSY-02", 2), (2, "WS008", "TEST-02", 3), (2, "WS009", "PKG-02", 4),
(3, "WS010", "SMT-03", 1), (3, "WS011", "DIP-03", 2), (3, "WS012", "TEST-03", 3), (3, "WS013", "PKG-03", 4),
]
for i, (line_id, code, name, seq) in enumerate(ws_rows): # 逐行展开为工位记录
data["workstations"].append({"id": i + 1, "lineId": line_id, "code": code, "name": name, "sequenceNo": seq, "status": "ACTIVE"})
# ---- 工序(5 道) ----
data["operations"] += [
{"id": 1, "code": "OP10", "name": "SMT贴片", "type": "INTERNAL", "standardTime": 0.5},
{"id": 2, "code": "OP20", "name": "DIP插件", "type": "INTERNAL", "standardTime": 0.8},
{"id": 3, "code": "OP30", "name": "组装", "type": "INTERNAL", "standardTime": 1.0},
{"id": 4, "code": "OP40", "name": "测试", "type": "INTERNAL", "standardTime": 0.6},
{"id": 5, "code": "OP50", "name": "包装", "type": "INTERNAL", "standardTime": 0.3},
]
# ---- 工艺路线(3 个产品各一条默认路线) ----
data["routings"] += [
{"id": 1, "productId": 1, "version": "V1.0", "versionName": "控制器A工艺V1.0", "isDefault": True, "status": "ACTIVE"},
{"id": 2, "productId": 2, "version": "V1.0", "versionName": "控制器B工艺V1.0", "isDefault": True, "status": "ACTIVE"},
{"id": 3, "productId": 3, "version": "V1.0", "versionName": "控制器C工艺V1.0", "isDefault": True, "status": "ACTIVE"},
]
# ---- 工艺步骤(routingId, operationId, 顺序, 前序, 准备分钟, 单件分钟, 等待, 转移, 是否外协) ----
step_rows = [
(1, 1, 1, None, 30, 0.5, 0, 10, False), (1, 2, 2, 1, 30, 0.8, 0, 10, False), (1, 3, 3, 2, 20, 1.0, 0, 10, False), (1, 4, 4, 3, 20, 0.6, 0, 10, False), (1, 5, 5, 4, 10, 0.3, 0, 10, False),
(2, 1, 1, None, 30, 0.4, 0, 10, False), (2, 3, 2, 1, 20, 0.9, 0, 10, False), (2, 4, 3, 2, 20, 0.5, 0, 10, False), (2, 5, 4, 3, 10, 0.3, 0, 10, False),
(3, 1, 1, None, 30, 0.6, 0, 10, False), (3, 2, 2, 1, 30, 1.0, 0, 10, False), (3, 4, 3, 2, 20, 0.7, 0, 10, False), (3, 5, 4, 3, 10, 0.3, 0, 10, False),
]
for i, (rid, oid, seq, prev, setup, run, wait, transfer, ext) in enumerate(step_rows): # 展开为步骤记录
data["routingSteps"].append({"id": i + 1, "routingId": rid, "operationId": oid, "sequenceNo": seq, "prevStepId": prev,
"setupTime": setup, "runTimePerUnit": run, "waitTime": wait, "transferTime": transfer, "isExternal": ext})
# ---- 物料(3 成品 + 7 原材料,库存/在途决定齐套结果) ----
data["materials"] += [
{"id": 1, "code": "CTRL-A", "name": "智能控制器A型", "spec": "标准版", "type": "FINISHED_PRODUCT", "unit": "件", "productFamily": "CTRL-STD", "safetyStock": 50, "procurementLeadTime": 0, "stock": 20, "inTransit": 0},
{"id": 2, "code": "CTRL-B", "name": "智能控制器B型", "spec": "经济版", "type": "FINISHED_PRODUCT", "unit": "件", "productFamily": "CTRL-STD", "safetyStock": 50, "procurementLeadTime": 0, "stock": 30, "inTransit": 0},
{"id": 3, "code": "CTRL-C", "name": "智能控制器C型", "spec": "高频版", "type": "FINISHED_PRODUCT", "unit": "件", "productFamily": "CTRL-HF", "safetyStock": 30, "procurementLeadTime": 0, "stock": 10, "inTransit": 0},
{"id": 10, "code": "PCB-001", "name": "标准PCB板", "spec": "FR-4", "type": "RAW_MATERIAL", "unit": "块", "safetyStock": 500, "procurementLeadTime": 7, "stock": 1800, "inTransit": 500},
{"id": 11, "code": "RC-001", "name": "电阻电容包", "spec": "混合包", "type": "RAW_MATERIAL", "unit": "包", "safetyStock": 300, "procurementLeadTime": 5, "stock": 900, "inTransit": 300},
{"id": 12, "code": "CASE-001", "name": "塑料外壳", "spec": "白色", "type": "RAW_MATERIAL", "unit": "个", "safetyStock": 400, "procurementLeadTime": 10, "stock": 1200, "inTransit": 0},
{"id": 13, "code": "SCR-001", "name": "螺丝包", "spec": "M3", "type": "RAW_MATERIAL", "unit": "包", "safetyStock": 200, "procurementLeadTime": 3, "stock": 800, "inTransit": 0},
{"id": 14, "code": "WIRE-001", "name": "线材", "spec": "22AWG", "type": "RAW_MATERIAL", "unit": "米", "safetyStock": 1000, "procurementLeadTime": 4, "stock": 3500, "inTransit": 0},
{"id": 15, "code": "LABEL-001", "name": "标签", "spec": "防伪", "type": "RAW_MATERIAL", "unit": "张", "safetyStock": 500, "procurementLeadTime": 2, "stock": 2500, "inTransit": 0},
{"id": 16, "code": "PCB-002", "name": "高频PCB板", "spec": "高频", "type": "RAW_MATERIAL", "unit": "块", "safetyStock": 200, "procurementLeadTime": 10, "stock": 350, "inTransit": 200},
]
# ---- MD-06 换型矩阵(产品族 × 产品族,分钟)----
data["changeoverMatrix"] += [
{"id": 1, "fromFamily": "CTRL-STD", "toFamily": "CTRL-HF", "setupMinutes": 45, "note": "标准→高频:换贴片程序+治具"},
{"id": 2, "fromFamily": "CTRL-HF", "toFamily": "CTRL-STD", "setupMinutes": 40, "note": "高频→标准:回退程序"},
]
# ---- BOM(3 个产品各一版默认 BOM) ----
data["boms"] += [
{"id": 1, "productId": 1, "version": "V2.1", "versionName": "A型BOM V2.1", "isDefault": True, "status": "ACTIVE"},
{"id": 2, "productId": 2, "version": "V1.0", "versionName": "B型BOM V1.0", "isDefault": True, "status": "ACTIVE"},
{"id": 3, "productId": 3, "version": "V1.0", "versionName": "C型BOM V1.0", "isDefault": True, "status": "ACTIVE"},
]
# ---- BOM 明细(bomId, materialId, 单件用量, 消耗工序, 是否关键料) ----
bom_rows = [
(1, 10, 1, 1, False), (1, 11, 1, 1, False), (1, 12, 1, 3, False), (1, 13, 1, 3, False), (1, 14, 0.5, 3, False), (1, 15, 1, 5, False),
(2, 10, 1, 1, False), (2, 11, 1.2, 1, False), (2, 12, 1, 3, False), (2, 13, 1, 3, False), (2, 14, 0.6, 3, False), (2, 15, 1, 5, False),
(3, 16, 1, 1, True), (3, 11, 1.5, 1, False), (3, 12, 1, 3, False), (3, 13, 1, 3, False), (3, 14, 0.4, 3, False), (3, 15, 1, 5, False),
]
for i, (bid, mid, qty, oid, key) in enumerate(bom_rows): # 展开为 BOM 明细
data["bomItems"].append({"id": i + 1, "bomId": bid, "materialId": mid, "quantity": qty, "operationId": oid, "isKeyMaterial": key})
# ---- 产线-产品配置(选线依据:priority 越小越优先) ----
data["lineProducts"] += [
{"id": 1, "lineId": 1, "productId": 1, "standardCapacity": 1000, "priority": 1, "setupTime": 30},
{"id": 2, "lineId": 1, "productId": 3, "standardCapacity": 600, "priority": 2, "setupTime": 40},
{"id": 3, "lineId": 2, "productId": 2, "standardCapacity": 800, "priority": 1, "setupTime": 30},
{"id": 4, "lineId": 2, "productId": 1, "standardCapacity": 700, "priority": 2, "setupTime": 40},
{"id": 5, "lineId": 3, "productId": 1, "standardCapacity": 500, "priority": 3, "setupTime": 45},
{"id": 6, "lineId": 3, "productId": 3, "standardCapacity": 600, "priority": 1, "setupTime": 30},
]
# ---- 工位-工序配置(工位能做哪道工序) ----
wso_rows = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 1), (7, 3), (8, 4), (9, 5), (10, 1), (11, 2), (12, 4), (13, 5)]
for i, (wsid, oid) in enumerate(wso_rows): # 展开为工位工序记录
data["workstationOperations"].append({"id": i + 1, "workstationId": wsid, "operationId": oid, "setupTime": 20, "runTimePerUnit": 0.5, "isPrimary": True})
# ---- 设备(每工位一台,参数用固定种子随机→确定性) ----
for i, ws in enumerate(data["workstations"]): # 遍历工位生成设备
data["equipment"].append({
"id": i + 1, "code": f"EQ{i + 1:03d}", "name": ws["name"] + "设备", "model": "MDL-" + ws["code"],
"workstationId": ws["id"], "capacityPerHour": 120,
"efficiencyFactor": round(0.95 + rng.random() * 0.05, 3), # 效率因子(确定性随机)
"availabilityRate": round(0.92 + rng.random() * 0.07, 3), # 可动率(确定性随机)
"status": "MAINTENANCE" if rng.random() > 0.9 else "RUNNING", # 少数设备处于保养态
})
# ---- 班组(6 个) ----
data["teams"] += [
{"id": 1, "code": "T001", "name": "贴片班组A", "workshopId": 1, "memberCount": 12, "skillLevel": "L3", "status": "ACTIVE"},
{"id": 2, "code": "T002", "name": "插件班组A", "workshopId": 1, "memberCount": 8, "skillLevel": "L2", "status": "ACTIVE"},
{"id": 3, "code": "T003", "name": "组装班组A", "workshopId": 1, "memberCount": 15, "skillLevel": "L3", "status": "ACTIVE"},
{"id": 4, "code": "T004", "name": "测试班组A", "workshopId": 1, "memberCount": 10, "skillLevel": "L4", "status": "ACTIVE"},
{"id": 5, "code": "T005", "name": "包装班组A", "workshopId": 1, "memberCount": 8, "skillLevel": "L2", "status": "ACTIVE"},
{"id": 6, "code": "T006", "name": "综合班组B", "workshopId": 1, "memberCount": 20, "skillLevel": "L3", "status": "ACTIVE"},
]
# ---- 班次(早班/中班,含休息段) ----
data["shifts"] += [
{"id": 1, "code": "D", "name": "早班", "startTime": "08:00", "endTime": "16:00", "breakPeriods": [{"start": "12:00", "end": "13:00"}], "isOvertime": False, "status": "ACTIVE"},
{"id": 2, "code": "N", "name": "中班", "startTime": "16:00", "endTime": "23:59", "breakPeriods": [{"start": "18:00", "end": "18:30"}], "isOvertime": False, "status": "ACTIVE"},
]
# ---- 班次日历(未来 30 天 × 产线 × 班次;周末不排班;覆盖引擎 30 天占槽窗口) ----
for i in range(30): # 未来 30 天
date = add_minutes(base, i * 24 * 60) # 第 i 天
date_str = fmt_date(date) # 日期字符串
is_weekend = date.weekday() >= 5 # 周六(5)/周日(6)
for line in data["lines"]: # 每条产线
for shift in data["shifts"]: # 每个班次
data["shiftCalendar"].append({
"id": len(data["shiftCalendar"]) + 1, # 顺序 ID
"lineId": line["id"], "date": date_str, "shiftId": shift["id"],
"isWorking": not is_weekend, # 周末停排
"teamId": 6 if line["id"] == 2 else (line["id"] if shift["id"] == 1 else 6), # 班组指派(演示口径)
"maxWorkers": 10, # 班次容量
})
# ---- 维保计划(第 3 天上午 ASSY-01 设备保养 → 制造设备冲突场景) ----
mnt_day = fmt_date(add_minutes(base, 2 * 24 * 60)) # 后天
data["maintenance"].append({"id": 1, "equipmentId": 3, "type": "MAINTENANCE",
"plannedStart": mnt_day + " 09:00", "plannedEnd": mnt_day + " 12:00",
"status": "PLANNED", "description": "ASSY-01 季度保养"})
# ---- 销售订单(7 张;日期相对今天偏移,保证任意运行日行为一致) ----
# 行结构:(客户, 等级, 下单偏移天, 交期偏移天, 优先级, 状态, 是否插单, 产品ID, 数量)
order_rows = [
("青岛科技", "A", -2, 9, 3, "APPROVED", False, 1, 500),
("海尔智家", "VIP", -3, 6, 1, "APPROVED", False, 2, 300),
("海信电子", "B", -2, 8, 5, "APPROVED", False, 1, 800),
("中车集团", "A", -4, 12, 4, "APPROVED", False, 3, 400),
("北汽新能源", "C", -1, 14, 8, "APPROVED", False, 2, 600),
("比亚迪", "VIP", -1, 4, 1, "APPROVED", True, 3, 200),
("青岛科技", "A", 0, 10, 3, "APPROVED", False, 2, 500),
]
for i, (cust, level, o_off, d_off, prio, status, rush, pid, qty) in enumerate(order_rows): # 展开为订单
order_date = fmt_date(add_minutes(base, o_off * 24 * 60)) # 下单日期(相对偏移)
delivery = fmt_date(add_minutes(base, d_off * 24 * 60)) # 交期(相对偏移)
product = next(m for m in data["materials"] if m["id"] == pid) # 关联产品
data["salesOrders"].append({
"id": i + 1,
"orderNo": "SO" + base.strftime("%Y%m%d") + f"{i + 1:03d}", # 订单号:SO+今天+序号
"customerId": f"CUST{i + 1:03d}", "customerName": cust, "customerLevel": level,
"orderDate": order_date, "deliveryDate": delivery,
"priority": prio, "manualPriority": None, "status": status,
"source": "MANUAL" if i % 2 == 0 else "API", # 来源交替(演示口径)
"specialRequirements": "需使用环保材料包装" if i == 0 else "",
"totalAmount": qty * (100 + (i * 37) % 200), # 金额(确定性伪随机)
"isRush": rush, "rushStrategy": "STRATEGY_SHIFT" if rush else None,
"changes": [], "createdBy": "admin",
"createdAt": order_date + " 09:00", "updatedAt": order_date + " 09:00",
"items": [{ # 单行订单项(M1 保持一单一项)
"id": i * 10 + 1, "orderId": i + 1, "lineNo": 1,
"productId": pid, "productName": product["name"], "productCode": product["code"],
"quantity": qty, "unit": "件", "bomVersion": "V1.0", "routingVersion": "V1.0",
"status": "PENDING", "note": "",
}],
})
# ---- OR-05 预测订单(长周期需求,默认不进正式排产)----
fc_rows = [
(1, 400, 14, 20, "WEEK", 0.75, "ACTIVE"),
(2, 300, 21, 27, "WEEK", 0.6, "ACTIVE"),
(3, 200, 30, 59, "MONTH", 0.5, "DRAFT"),
]
for i, (pid, qty, s_off, e_off, bucket, conf, st) in enumerate(fc_rows):
product = next(m for m in data["materials"] if m["id"] == pid)
ps = fmt_date(add_minutes(base, s_off * 24 * 60))
pe = fmt_date(add_minutes(base, e_off * 24 * 60))
data["forecastOrders"].append({
"id": i + 1,
"forecastNo": "FC" + base.strftime("%Y%m%d") + f"{i + 1:03d}",
"productId": pid, "productCode": product["code"], "productName": product["name"],
"quantity": qty, "unit": "件", "bucket": bucket,
"periodStart": ps, "periodEnd": pe, "dueDate": pe,
"confidence": conf, "status": st,
"customerName": "预测需求", "customerLevel": "C",
"note": "种子预测", "source": "SEED",
"createdAt": fmt_dt(base), "updatedAt": fmt_dt(base), "convertedOrderNo": None,
})
# ---- 初始日志(审计雏形 §3.6:系统初始化事件) ----
data["logs"].append({"id": 1, "type": "INFO", "category": "SCHEDULE", "operationType": "SYSTEM",
"targetType": "SCHEDULE", "targetId": 1, "action": "系统初始化",
"description": "种子数据生成完成", "operator": "system", "createdAt": fmt_dt(base)})
# ============================================================
# 柔性排产层(flex*):设备能力池 + 虚拟产线(吸收 demand/ 康尼芜湖方案)
# 与固定产线层并行、独立数据键;不进 RULE 引擎与既有黄金测试计数。
# 字段口径见 docs/product/demand-data-intake.md。案例形态,非绑定真实客户。
# ============================================================
_seed_flex(data, base)
return data # 返回完整世界状态
def ensure_flex_seed(data: dict[str, Any]) -> bool:
"""幂等补种:旧世界(flex 层加入前落盘的 world.json)缺柔性主数据时补齐。
Returns: True 表示本次执行了补种(调用方可据此决定是否落盘)。
现场导入清空演示后(flexParams.demoDataCleared / siteProfile)不再回填假数据。
"""
if data.get("flexOrders"): # 已有柔性订单 → 无需补种
return False
params = data.get("flexParams") or {}
if params.get("demoDataCleared") or params.get("siteProfile"):
return False
_seed_flex(data, today0()) # 以今天为相对基准补齐 flex* 段
return True
def _seed_flex(data: dict[str, Any], base) -> None:
"""柔性资源模型演示数据(吸收 demand/ 康尼芜湖方案 V1.0)。
4 类共线产品(高压线束总成 / PDU 配电单元 / 充电枪总成 / 高压连接器),
7 个能力池(下线·压接·装配·测试·焊接·气密·包装),压接机 4 台可跨区调配。
案例形态,非绑定真实客户;字段口径见 docs/product/demand-data-intake.md。
"""
# ---- 布局区域(U 型线束区 / 直线 PDU 区 / ESD 充电枪区 / 公共测试包装区) ----
data["flexZones"] = [
{"code": "ZONE-A", "name": "线束区(U型)"},
{"code": "ZONE-B", "name": "PDU区(直线)"},
{"code": "ZONE-D", "name": "充电枪区(ESD)"},
{"code": "ZONE-C", "name": "公共测试与包装区"},
]
# ---- 工序(含瓶颈标记 + 换型时间;四类产品各自瓶颈见 demand §6.1) ----
data["flexOperations"] = [
{"code": "OP-IQC", "name": "来料检验", "isBottleneck": False, "changeoverMin": 0},
{"code": "OP-CUT", "name": "下线剥皮", "isBottleneck": False, "changeoverMin": 10},
{"code": "OP-CRIMP", "name": "压接端子", "isBottleneck": True, "changeoverMin": 20}, # 线束瓶颈
{"code": "OP-WELD", "name": "激光焊接", "isBottleneck": True, "changeoverMin": 25}, # PDU 瓶颈(全厂最紧)
{"code": "OP-LEAK", "name": "气密检漏", "isBottleneck": False, "changeoverMin": 10},
{"code": "OP-ESD", "name": "控制盒装配(ESD)", "isBottleneck": True, "changeoverMin": 15}, # 充电枪瓶颈
{"code": "OP-PULL", "name": "插拔力测试", "isBottleneck": True, "changeoverMin": 5}, # 连接器瓶颈
{"code": "OP-ASSY", "name": "装配", "isBottleneck": False, "changeoverMin": 15},
{"code": "OP-TEST", "name": "综合测试", "isBottleneck": False, "changeoverMin": 5},
{"code": "OP-PKG", "name": "贴标包装", "isBottleneck": False, "changeoverMin": 5},
]
# ---- 设备(能力集 + 可移动 + 当前区域 + 适配模具 + 可动率),对齐 demand §3.2 产能池 ----
# 行:(编码, 名称, 能力[工序:单件分钟...], 可移动, 移动耗时, 区域, 适配模具, 可动率)
eq_rows = [
# 线束区:2 下线机 + 4 压接机(可移动) + 2 装配台
("CUT-01", "全自动下线机#1", {"OP-CUT": 2}, False, 0, "ZONE-A", [], 0.95),
("CUT-02", "全自动下线机#2", {"OP-CUT": 2}, False, 0, "ZONE-A", [], 0.95),
("PRESS-01", "气动压接机#1", {"OP-CRIMP": 1}, True, 30, "ZONE-A", ["MOLD-HV-01", "MOLD-HV-02", "MOLD-HV-03", "MOLD-CG-01"], 0.94),
("PRESS-02", "气动压接机#2", {"OP-CRIMP": 1}, True, 30, "ZONE-A", ["MOLD-HV-01", "MOLD-HV-02", "MOLD-CG-01", "MOLD-CG-02"], 0.94),
("PRESS-03", "气动压接机#3", {"OP-CRIMP": 1}, True, 30, "ZONE-A", ["MOLD-HV-01", "MOLD-HV-03", "MOLD-CG-01"], 0.93),
("PRESS-04", "气动压接机#4", {"OP-CRIMP": 1}, True, 30, "ZONE-A", ["MOLD-HV-02", "MOLD-CG-01", "MOLD-CG-02"], 0.93),
("ASSY-01", "装配台#1", {"OP-ASSY": 5}, False, 0, "ZONE-A", [], 0.96),
("ASSY-02", "装配台#2", {"OP-ASSY": 5}, False, 0, "ZONE-A", [], 0.96),
# PDU 区:激光焊接机 ×1 + 氦质谱/气密检测 ×2 + 装配台 ×1
("WELD-01", "激光焊接机#1", {"OP-WELD": 5}, False, 0, "ZONE-B", [], 0.92),
("LEAK-01", "氦质谱检漏仪#1", {"OP-LEAK": 4}, False, 0, "ZONE-B", [], 0.95),
("LEAK-02", "气密检测仪#1", {"OP-LEAK": 4}, False, 0, "ZONE-B", [], 0.95),
("ASSY-03", "装配台#3", {"OP-ASSY": 5}, False, 0, "ZONE-B", [], 0.96),
# 充电枪区:ESD 控制盒装配台 ×2
("ESD-01", "ESD控制盒装配台#1", {"OP-ESD": 5}, False, 0, "ZONE-D", [], 0.95),
("ESD-02", "ESD控制盒装配台#2", {"OP-ESD": 5}, False, 0, "ZONE-D", [], 0.95),
# 公共测试与包装区
("IQC-01", "来料检验台#1", {"OP-IQC": 3}, False, 0, "ZONE-C", [], 0.98),
("TEST-01", "高压测试台#1", {"OP-TEST": 2}, False, 0, "ZONE-C", [], 0.97),
("TEST-02", "高压测试台#2", {"OP-TEST": 2}, False, 0, "ZONE-C", [], 0.97),
("TEST-03", "PDU测试台#1", {"OP-TEST": 3}, False, 0, "ZONE-C", [], 0.97),
("TEST-04", "充电枪测试台#1", {"OP-TEST": 3}, False, 0, "ZONE-C", [], 0.97),
("PULL-01", "插拔力测试机#1", {"OP-PULL": 2}, False, 0, "ZONE-C", [], 0.95),
("PKG-01", "包装台#1", {"OP-PKG": 1}, False, 0, "ZONE-C", [], 0.99),
("PKG-02", "包装台#2", {"OP-PKG": 1}, False, 0, "ZONE-C", [], 0.99),
]
data["flexEquipment"] = []
for i, (code, name, cap, movable, mv, zone, molds, avail) in enumerate(eq_rows):
data["flexEquipment"].append({
"id": i + 1, "code": code, "name": name,
"capabilities": list(cap.keys()), # 能力集(工序编码)
"opStdTime": cap, # 各工序单件工时(分钟)
"movable": movable, "moveTimeMin": mv, "zone": zone,
"adaptableMolds": molds, "availabilityRate": avail, "status": "RUNNING",
})
# ---- 工装/模具(压接模具家族;demand 提及 50+ 套,此处抽样 5 副代表) ----
mold_rows = [
("MOLD-HV-01", "高压端子模具A", "OP-CRIMP", ["PRESS-01", "PRESS-02", "PRESS-03"], 50000, 12000, "ZONE-A", 20),
("MOLD-HV-02", "高压端子模具B", "OP-CRIMP", ["PRESS-01", "PRESS-02", "PRESS-04"], 50000, 30000, "ZONE-A", 20),
("MOLD-HV-03", "高压端子模具C", "OP-CRIMP", ["PRESS-01", "PRESS-03"], 50000, 5000, "ZONE-A", 18),
("MOLD-CG-01", "充电枪端子模具A", "OP-CRIMP", ["PRESS-02", "PRESS-04"], 40000, 8000, "ZONE-A", 25),
("MOLD-CG-02", "充电枪端子模具B", "OP-CRIMP", ["PRESS-03", "PRESS-04"], 40000, 15000, "ZONE-A", 25),
]
data["flexMolds"] = []
for i, (code, name, op, eqs, total, used, zone, ch) in enumerate(mold_rows):
data["flexMolds"].append({
"id": i + 1, "code": code, "name": name, "operationCode": op,
"adaptableEquipment": eqs, "lifeTotal": total, "lifeUsed": used,
"zone": zone, "changeoverMin": ch, "status": "AVAILABLE",
})
# ---- 物料(4 成品 + 关键原料,含库存/在途/前置期) ----
data["flexMaterials"] = [
{"code": "HV-HARNESS", "name": "高压线束总成", "type": "FINISHED_PRODUCT", "unit": "套", "stock": 0, "inTransit": 0, "safetyStock": 0, "procurementLeadTime": 0},
{"code": "PDU-UNIT", "name": "PDU配电单元", "type": "FINISHED_PRODUCT", "unit": "套", "stock": 0, "inTransit": 0, "safetyStock": 0, "procurementLeadTime": 0},
{"code": "CHARGE-GUN", "name": "充电枪总成", "type": "FINISHED_PRODUCT", "unit": "套", "stock": 0, "inTransit": 0, "safetyStock": 0, "procurementLeadTime": 0},
{"code": "HV-CONN", "name": "高压连接器", "type": "FINISHED_PRODUCT", "unit": "个", "stock": 0, "inTransit": 0, "safetyStock": 0, "procurementLeadTime": 0},
{"code": "WIRE-HV", "name": "高压线材", "type": "RAW_MATERIAL", "unit": "米", "stock": 12000, "inTransit": 0, "safetyStock": 1000, "procurementLeadTime": 7},
{"code": "TERM-HV", "name": "高压端子", "type": "RAW_MATERIAL", "unit": "个", "stock": 6000, "inTransit": 2000, "safetyStock": 500, "procurementLeadTime": 10},
{"code": "BUSBAR", "name": "铜排", "type": "RAW_MATERIAL", "unit": "根", "stock": 1500, "inTransit": 400, "safetyStock": 200, "procurementLeadTime": 14},
{"code": "CONN-PDU", "name": "PDU连接器", "type": "RAW_MATERIAL", "unit": "个", "stock": 1600, "inTransit": 0, "safetyStock": 200, "procurementLeadTime": 12},
{"code": "CTRL-BOX", "name": "充电枪控制盒", "type": "RAW_MATERIAL", "unit": "个", "stock": 900, "inTransit": 0, "safetyStock": 100, "procurementLeadTime": 15},
{"code": "TERM-CG", "name": "充电枪端子", "type": "RAW_MATERIAL", "unit": "个", "stock": 4000, "inTransit": 0, "safetyStock": 300, "procurementLeadTime": 9},
{"code": "CONN-HOUSING", "name": "连接器壳体", "type": "RAW_MATERIAL", "unit": "个", "stock": 2000, "inTransit": 0, "safetyStock": 200, "procurementLeadTime": 8},
{"code": "PIN-HV", "name": "高压插针", "type": "RAW_MATERIAL", "unit": "个", "stock": 6000, "inTransit": 0, "safetyStock": 500, "procurementLeadTime": 6},
]
# ---- BOM(成品 → 物料,含消耗工序/关键料) ----
data["flexBom"] = [
{"productCode": "HV-HARNESS", "materialCode": "WIRE-HV", "quantity": 2.0, "consumeOp": "OP-CUT", "isKey": False},
{"productCode": "HV-HARNESS", "materialCode": "TERM-HV", "quantity": 4.0, "consumeOp": "OP-CRIMP", "isKey": True},
{"productCode": "PDU-UNIT", "materialCode": "BUSBAR", "quantity": 3.0, "consumeOp": "OP-WELD", "isKey": True},
{"productCode": "PDU-UNIT", "materialCode": "CONN-PDU", "quantity": 2.0, "consumeOp": "OP-ASSY", "isKey": False},
{"productCode": "CHARGE-GUN", "materialCode": "CTRL-BOX", "quantity": 1.0, "consumeOp": "OP-ESD", "isKey": True},
{"productCode": "CHARGE-GUN", "materialCode": "TERM-CG", "quantity": 3.0, "consumeOp": "OP-CRIMP", "isKey": False},
{"productCode": "HV-CONN", "materialCode": "CONN-HOUSING", "quantity": 1.0, "consumeOp": "OP-ASSY", "isKey": False},
{"productCode": "HV-CONN", "materialCode": "PIN-HV", "quantity": 6.0, "consumeOp": "OP-ASSY", "isKey": True},
]
# ---- 工艺路线(产品 → 有序工序;requireMold 决定是否需占模具) ----
# 行:(产品, 产品名, seq, 工序, 是否需模具, 单件工时覆盖 或 None)
rt_rows = [
# 高压线束总成:来料→下线→压接(模具)→装配→测试→包装
("HV-HARNESS", "高压线束总成", 10, "OP-IQC", False, None),
("HV-HARNESS", "高压线束总成", 20, "OP-CUT", False, None),
("HV-HARNESS", "高压线束总成", 30, "OP-CRIMP", True, None),
("HV-HARNESS", "高压线束总成", 40, "OP-ASSY", False, None),
("HV-HARNESS", "高压线束总成", 50, "OP-TEST", False, None),
("HV-HARNESS", "高压线束总成", 60, "OP-PKG", False, None),
# PDU 配电单元:来料→激光焊接→气密检漏→装配→测试→包装
("PDU-UNIT", "PDU配电单元", 10, "OP-IQC", False, None),
("PDU-UNIT", "PDU配电单元", 20, "OP-WELD", False, None),
("PDU-UNIT", "PDU配电单元", 30, "OP-LEAK", False, None),
("PDU-UNIT", "PDU配电单元", 40, "OP-ASSY", False, None),
("PDU-UNIT", "PDU配电单元", 50, "OP-TEST", False, None),
("PDU-UNIT", "PDU配电单元", 60, "OP-PKG", False, None),
# 充电枪总成:来料→压接(模具)→控制盒装配(ESD)→装配→测试→包装(与线束共用压接池)
("CHARGE-GUN", "充电枪总成", 10, "OP-IQC", False, None),
("CHARGE-GUN", "充电枪总成", 20, "OP-CRIMP", True, None),
("CHARGE-GUN", "充电枪总成", 30, "OP-ESD", False, None),
("CHARGE-GUN", "充电枪总成", 40, "OP-ASSY", False, None),
("CHARGE-GUN", "充电枪总成", 50, "OP-TEST", False, None),
("CHARGE-GUN", "充电枪总成", 60, "OP-PKG", False, None),
# 高压连接器:来料→装配→插拔力测试→包装
("HV-CONN", "高压连接器", 10, "OP-IQC", False, None),
("HV-CONN", "高压连接器", 20, "OP-ASSY", False, None),
("HV-CONN", "高压连接器", 30, "OP-PULL", False, None),
("HV-CONN", "高压连接器", 40, "OP-PKG", False, None),
]
data["flexRoutings"] = [
{"productCode": pc, "productName": pn, "seq": seq, "operationCode": op, "requireMold": rm, "stdTimePerUnit": st}
for (pc, pn, seq, op, rm, st) in rt_rows
]
# ---- 班组(人力资格;MVP 记录但排产先按设备约束) ----
data["flexTeams"] = [
{"code": "T-CUT", "name": "下线班组", "memberCount": 4, "supportOps": ["OP-CUT"], "skillLevel": "L2"},
{"code": "T-CRIMP", "name": "压接班组", "memberCount": 6, "supportOps": ["OP-CRIMP", "OP-ASSY"], "skillLevel": "L3"},
{"code": "T-WELD", "name": "焊接气密班组", "memberCount": 3, "supportOps": ["OP-WELD", "OP-LEAK"], "skillLevel": "L4"},
{"code": "T-ESD", "name": "充电枪装配班组", "memberCount": 4, "supportOps": ["OP-ESD", "OP-ASSY"], "skillLevel": "L3"},
{"code": "T-TEST", "name": "测试包装班组", "memberCount": 6, "supportOps": ["OP-TEST", "OP-PULL", "OP-PKG", "OP-IQC"], "skillLevel": "L3"},
]
# ---- 班次日历(单班 08:00-17:00,午休 1h;工作日周一~周五) ----
data["flexCalendar"] = [
{"shiftCode": "D", "startTime": "08:00", "endTime": "17:00",
"breaks": [{"start": "12:00", "end": "13:00"}], "workdays": [1, 2, 3, 4, 5]},
]
# ---- 订单(多品种小批量:4 产品 10 单,交期/优先级错落;含 WBS 与生产控制者隔离字段) ----
day = base
def _d(off: int) -> str:
return fmt_date(add_minutes(day, off * 24 * 60))
order_rows = [
("FO-2601", "HV-HARNESS", 100, _d(6), 2, "WBS-HV", "李强", "RELEASED"),
("FO-2602", "HV-HARNESS", 60, _d(4), 1, "WBS-HV", "李强", "RELEASED"),
("FO-2603", "PDU-UNIT", 80, _d(5), 1, "WBS-PDU", "王芳", "RELEASED"),
("FO-2604", "PDU-UNIT", 120, _d(8), 3, "WBS-PDU", "王芳", "RELEASED"),
("FO-2605", "CHARGE-GUN", 50, _d(5), 2, "WBS-CG", "陈涛", "RELEASED"),
("FO-2606", "CHARGE-GUN", 90, _d(9), 3, "WBS-CG", "陈涛", "RELEASED"),
("FO-2607", "HV-CONN", 200, _d(4), 1, "WBS-CONN", "赵敏", "RELEASED"),
("FO-2608", "HV-CONN", 150, _d(7), 2, "WBS-CONN", "赵敏", "RELEASED"),
("FO-2609", "HV-HARNESS", 200, _d(11), 4, "WBS-HV", "李强", "CREATED"),
("FO-2610", "PDU-UNIT", 60, _d(10), 4, "WBS-PDU", "王芳", "CREATED"),
]
data["flexOrders"] = [
{"id": i + 1, "orderNo": no, "productCode": pc, "quantity": qty, "dueDate": due,
"priority": prio, "wbs": wbs, "productionController": pcr, "status": st}
for i, (no, pc, qty, due, prio, wbs, pcr, st) in enumerate(order_rows)
]
# ---- 柔性排产参数(滚动时域 + 排产模式默认) ----
data["flexParams"] = {
"sortMode": "BOTTLENECK", # ASC 正排 / DESC 倒排 / BOTTLENECK 瓶颈锚
"beforeDays": 0, "afterDays": 30,
"rollingWindows": {"realtime": "60m", "long": "7d", "mid": "2d", "short": "2h"},
"mrpControllerDays": 3,
"weights": {"tardiness": 0.4, "cost": 0.3, "utilization": 0.2, "balance": 0.1},
}
# 柔性排产产物表(引擎写入;与固定产线层的 scheduleVersions 分离)
data["flexScheduleVersions"] = []
data["flexVirtualLines"] = []
data["flexWorkOrders"] = []
data["flexConflicts"] = []