Spaces:
Sleeping
Sleeping
import pandas as pd | |
from itertools import permutations | |
from fastapi import FastAPI, File, UploadFile, Form, HTTPException | |
from fastapi.middleware.cors import CORSMiddleware | |
from pydantic import BaseModel | |
from typing import List | |
app = FastAPI() | |
app.add_middleware( | |
CORSMiddleware, | |
allow_origins=["*"], # Allows all origins | |
allow_credentials=True, | |
allow_methods=["*"], # Allows all methods | |
allow_headers=["*"], # Allows all headers | |
) | |
class Box: | |
def __init__(self, length, width, height, quantity, box_type): | |
self.length = length | |
self.width = width | |
self.height = height | |
self.quantity = quantity if quantity > 0 else 1 # Ensure at least 1 box if quantity is not specified | |
self.type = box_type | |
def rotated_dimensions(self): | |
# Return rotations that maximize base area and minimize height | |
possible_rotations = [ | |
(self.length, self.width, self.height), | |
(self.length, self.height, self.width), | |
(self.width, self.length, self.height), | |
(self.width, self.height, self.length), | |
(self.height, self.length, self.width), | |
(self.height, self.width, self.length) | |
] | |
# Sort by volume and base area to prioritize the best fit | |
return sorted(possible_rotations, key=lambda x: (x[0] * x[1], x[2]), reverse=True) | |
def volume(self): | |
return self.length * self.width * self.height | |
class Truck: | |
def __init__(self, length, width, height): | |
self.length = length | |
self.width = width | |
self.height = height | |
self.volume = length * width * height | |
self.placed_boxes = [] | |
self.available_space = [(0, 0, 0, length, width, height)] # (x, y, z, l, w, h) | |
def add_box(self, box): | |
best_fit = None | |
best_fit_space_index = -1 | |
for rotation in box.rotated_dimensions(): | |
for i, space in enumerate(self.available_space): | |
x, y, z, l, w, h = space | |
# Check if the box can fit in the current space | |
if rotation[0] <= l and rotation[1] <= w and rotation[2] <= h: | |
if not best_fit or (rotation[0] * rotation[1] > best_fit[0] * best_fit[1]): | |
best_fit = rotation | |
best_fit_space_index = i | |
if best_fit: | |
x, y, z, l, w, h = self.available_space[best_fit_space_index] | |
box_position = (x, y, z) | |
# Place the box in the truck | |
self.placed_boxes.append((best_fit, box_position)) | |
# Update available space after placing the box | |
self.available_space.pop(best_fit_space_index) | |
self.update_space(best_fit, box_position, l, w, h) | |
return box_position | |
else: | |
return None | |
def update_space(self, box, position, l, w, h): | |
x, y, z = position | |
bl, bw, bh = box | |
# Create new spaces based on the placement of the new box | |
new_spaces = [ | |
(x + bl, y, z, l - bl, w, h), # Space to the right | |
(x, y + bw, z, bl, w - bw, h), # Space in front | |
(x, y, z + bh, bl, bw, h - bh), # Space above | |
] | |
# Filter out invalid spaces | |
new_spaces = [space for space in new_spaces if space[3] > 0 and space[4] > 0 and space[5] > 0] | |
# Add new valid spaces to the available_space list | |
self.available_space.extend(new_spaces) | |
# Sort available spaces to prioritize lower and more central spaces for stacking | |
self.available_space.sort(key=lambda space: (space[2], space[1], space[0])) | |
def pack_boxes(truck, boxes): | |
packed_positions = [] | |
# Sort all boxes by volume (largest first) | |
boxes.sort(key=lambda b: b.volume(), reverse=True) | |
# Pack all boxes, ensuring practical stacking | |
for box in boxes: | |
for _ in range(box.quantity): | |
position = truck.add_box(box) | |
if position is None: | |
break | |
packed_positions.append((box, position)) | |
return packed_positions | |
async def upload_file( | |
file: UploadFile = File(...), | |
length: float = Form(...), | |
width: float = Form(...), | |
height: float = Form(...), | |
): | |
if not file: | |
raise HTTPException(status_code=400, detail="No file uploaded") | |
ext = file.filename.split('.')[-1].lower() | |
if ext == 'csv': | |
data = pd.read_csv(file.file) | |
elif ext in ['xls', 'xlsx']: | |
data = pd.read_excel(file.file) | |
else: | |
raise HTTPException(status_code=400, detail="Unsupported file format") | |
# Convert dimensions from CM to inches | |
data['PieceLength'] = data['PieceLength'] / 2.54 | |
data['PieceBreadth'] = data['PieceBreadth'] / 2.54 | |
data['PieceHeight'] = data['PieceHeight'] / 2.54 | |
# Create Box objects with quantity considered | |
boxes = [ | |
Box(row['PieceLength'], row['PieceBreadth'], row['PieceHeight'], row.get('Quantity', 1), f"{row['PieceNo']}-{row['Priority']}") | |
for _, row in data.iterrows() | |
] | |
# Convert truck dimensions from feet to inches | |
truck_length = length * 12 # Convert to inches | |
truck_width = width * 12 # Convert to inches | |
truck_height = height * 12 # Convert to inches | |
truck = Truck(truck_length, truck_width, truck_height) | |
# Pack the boxes into the truck | |
packed_positions = pack_boxes(truck, boxes) | |
box_data = [ | |
{ | |
'length': box.length, | |
'width': box.width, | |
'height': box.height, | |
'type': box.type, | |
'quantity': box.quantity, | |
'position': {'x': pos[0], 'y': pos[1], 'z': pos[2]} | |
} | |
for box, pos in packed_positions | |
] | |
print(f"quantity {[box_data[i]['quantity'] for i in range(len(box_data))]}") | |
return {"boxes": box_data} | |