"""一维优化下料服务:首递减(FFD)装箱算法,输出切割方案与利用率。 对齐目标站 optimize1d 语义:输入订单型材需求 + 库存原料,输出每根原料的切割组合。 """ from dataclasses import dataclass, field @dataclass class BarDemand: """一段型材需求。""" length: int # 需求长度(mm) qty: int # 数量 label: str = "" # 用途标签 @dataclass class StockBar: """库存原料。""" length: int # 原料长度(mm) qty: int # 库存根数 label: str = "" @dataclass class CutPiece: length: int label: str = "" @dataclass class CutPlan: """一根原料的切割方案。""" stock_length: int stock_label: str pieces: list[CutPiece] = field(default_factory=list) waste: int = 0 # 余料浪费 # 锯口损耗(mm),每次切割损耗一段 KERF_DEFAULT = 5 def optimize_1d(demands: list[BarDemand], stocks: list[StockBar], kerf: int = KERF_DEFAULT) -> dict: """首递减装箱:长件优先装入可容纳的原料,输出方案与统计。""" # 展开需求并降序 pieces: list[CutPiece] = [] for d in demands: pieces.extend(CutPiece(length=d.length, label=d.label) for _ in range(max(0, d.qty))) pieces.sort(key=lambda p: p.length, reverse=True) # 展开库存(可切割长度 = 原料长) avail: list[dict] = [] for s in stocks: for _ in range(max(0, s.qty)): avail.append({"length": s.length, "label": s.label, "pieces": [], "used": 0}) unplaced: list[CutPiece] = [] for piece in pieces: # 找第一根装得下的原料(剩余空间 >= 件长 + 锯口) placed = False for bar in avail: remain = bar["length"] - bar["used"] cost = piece.length + (kerf if bar["pieces"] else 0) if remain >= cost: bar["pieces"].append(piece) bar["used"] += cost placed = True break if not placed: unplaced.append(piece) plans = [ CutPlan( stock_length=b["length"], stock_label=b["label"], pieces=b["pieces"], waste=b["length"] - b["used"], ) for b in avail if b["pieces"] ] total_demand = sum(p.length for p in pieces) total_stock = sum(p.stock_length for p in plans) return { "plans": [ { "stock_length": p.stock_length, "stock_label": p.stock_label, "pieces": [{"length": c.length, "label": c.label} for c in p.pieces], "waste": p.waste, } for p in plans ], "unplaced": [{"length": c.length, "label": c.label} for c in unplaced], "stats": { "total_pieces": len(pieces), "placed_pieces": len(pieces) - len(unplaced), "stock_bars_used": len(plans), "total_demand_mm": total_demand, "total_stock_mm": total_stock, "utilization": round(total_demand / total_stock, 4) if total_stock else 0, "kerf_mm": kerf, }, }