feat: P1批次3 生产域(车间管理/扫码跟踪/双通道优化/订单利润/条码打印)
This commit is contained in:
@@ -104,3 +104,91 @@ def optimize_1d(demands: list[BarDemand], stocks: list[StockBar],
|
||||
"kerf_mm": kerf,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def optimize_1d_fine(demands: list[BarDemand], stocks: list[StockBar],
|
||||
kerf: int = KERF_DEFAULT) -> dict:
|
||||
"""详细优化(对齐源站 optimize1d-ai「详细优化」语义):BFD 最佳适应递减 + 余料均衡二次调整。
|
||||
|
||||
相比快速优化(FFD):每件放入"装下后剩余最小"的原料,显著减少大余料浪费;
|
||||
再做一轮跨原料的余料均衡交换(把某根的最后一件换到余料更贴合的原料)。
|
||||
"""
|
||||
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:
|
||||
# BFD: 选装下后剩余最小的原料
|
||||
best, best_remain = None, None
|
||||
for bar in avail:
|
||||
remain = bar["length"] - bar["used"]
|
||||
cost = piece.length + (kerf if bar["pieces"] else 0)
|
||||
if remain >= cost:
|
||||
r = remain - cost
|
||||
if best is None or r < best_remain:
|
||||
best, best_remain = bar, r
|
||||
if best is not None:
|
||||
best["pieces"].append(piece)
|
||||
best["used"] += piece.length + (kerf if len(best["pieces"]) > 1 else 0)
|
||||
else:
|
||||
unplaced.append(piece)
|
||||
|
||||
# 余料均衡(有界 2 轮):把"仅一件原料"上的件移到另一根更贴合的原料,减少总浪费
|
||||
for _round in range(2):
|
||||
moved = False
|
||||
for src in avail:
|
||||
if len(src["pieces"]) != 1:
|
||||
continue
|
||||
last = src["pieces"][0]
|
||||
src_used = last.length # 单件无锯口
|
||||
for dst in avail:
|
||||
if dst is src or not dst["pieces"]:
|
||||
continue
|
||||
cost = last.length + kerf
|
||||
if dst["length"] - dst["used"] >= cost:
|
||||
new_dst_remain = dst["length"] - dst["used"] - cost
|
||||
if new_dst_remain < src["length"] - src_used:
|
||||
dst["pieces"].append(last)
|
||||
dst["used"] += cost
|
||||
src["pieces"].pop()
|
||||
src["used"] = 0
|
||||
moved = True
|
||||
break
|
||||
if moved:
|
||||
break
|
||||
if not moved:
|
||||
break
|
||||
|
||||
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)
|
||||
placed_demand = total_demand - sum(p.length for p in unplaced)
|
||||
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(placed_demand / total_stock, 4) if total_stock else 0,
|
||||
"kerf_mm": kerf,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user