File size: 5,865 Bytes
6a808d5
 
72726e0
 
 
 
6a808d5
72726e0
 
 
 
 
 
 
 
 
6a808d5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72726e0
 
 
 
 
 
 
6a808d5
72726e0
6a808d5
 
 
72726e0
6a808d5
72726e0
6a808d5
72726e0
6a808d5
 
 
 
 
 
 
 
 
 
 
 
 
72726e0
 
 
6a808d5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72726e0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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

@app.post("/upload/")
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}