474 lines
35 KiB
Python
474 lines
35 KiB
Python
# ============================================================
|
||
# 种子数据工厂(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"] = []
|