305 lines
9.2 KiB
Go
305 lines
9.2 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"math"
|
||
"net/http"
|
||
)
|
||
|
||
type Container struct {
|
||
Length float64 `json:"length"`
|
||
Width float64 `json:"width"`
|
||
Height float64 `json:"height"`
|
||
WeightLimit float64 `json:"weightLimit"`
|
||
}
|
||
|
||
type Box struct {
|
||
Length float64 `json:"length"`
|
||
Width float64 `json:"width"`
|
||
Height float64 `json:"height"`
|
||
Weight float64 `json:"weight"`
|
||
}
|
||
|
||
type Placement struct {
|
||
X float64 `json:"x"`
|
||
Y float64 `json:"y"`
|
||
Z float64 `json:"z"`
|
||
RotationX float64 `json:"rotationX"`
|
||
RotationY float64 `json:"rotationY"`
|
||
RotationZ float64 `json:"rotationZ"`
|
||
BoxNumber int `json:"boxNumber"`
|
||
}
|
||
|
||
type Layer struct {
|
||
Count int `json:"count"`
|
||
Layout []Placement `json:"layout"`
|
||
}
|
||
|
||
func main() {
|
||
fmt.Println("集装箱装箱系统已启动,监听端口 :8080")
|
||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||
http.ServeFile(w, r, "index.html")
|
||
})
|
||
http.HandleFunc("/calculate", func(w http.ResponseWriter, r *http.Request) {
|
||
var data struct {
|
||
Container Container `json:"container"`
|
||
Box Box `json:"box"`
|
||
}
|
||
err := json.NewDecoder(r.Body).Decode(&data)
|
||
if err != nil {
|
||
http.Error(w, "参数解析失败", http.StatusBadRequest)
|
||
fmt.Printf("⚠️ 错误:参数解析失败 - %v\n", err)
|
||
return
|
||
}
|
||
fmt.Printf("🚀 收到请求:\n集装箱参数:长%fmm × 宽%fmm × 高%fmm(承重%fkg)\n纸箱参数:长%fmm × 宽%fmm × 高%fmm(重量%fkg)\n",
|
||
data.Container.Length, data.Container.Width, data.Container.Height, data.Container.WeightLimit,
|
||
data.Box.Length, data.Box.Width, data.Box.Height, data.Box.Weight)
|
||
|
||
layout, strategy, count := optimizePacking(data.Container, data.Box)
|
||
layerMap := make(map[float64][]Placement)
|
||
for _, pos := range layout {
|
||
layerMap[pos.Z] = append(layerMap[pos.Z], pos)
|
||
}
|
||
var layers []Layer
|
||
for _, layer := range layerMap {
|
||
layers = append(layers, Layer{
|
||
Count: len(layer),
|
||
Layout: layer,
|
||
})
|
||
}
|
||
response := struct {
|
||
Count int `json:"count"`
|
||
Layers []Layer `json:"layers"`
|
||
Strategy string `json:"strategy"`
|
||
SpaceUtilization float64 `json:"spaceUtilization"`
|
||
TotalWeight float64 `json:"totalWeight"`
|
||
BoxLength float64 `json:"boxLength"`
|
||
BoxWidth float64 `json:"boxWidth"`
|
||
BoxHeight float64 `json:"boxHeight"`
|
||
}{
|
||
Count: count,
|
||
Layers: layers,
|
||
Strategy: strategy,
|
||
SpaceUtilization: calculateDensity(data.Container, data.Box, count),
|
||
TotalWeight: float64(count) * data.Box.Weight,
|
||
BoxLength: data.Box.Length,
|
||
BoxWidth: data.Box.Width,
|
||
BoxHeight: data.Box.Height,
|
||
}
|
||
json.NewEncoder(w).Encode(response)
|
||
fmt.Printf("✅ 返回结果:\n最优装箱数:%d\n策略:%s\n空间利用率:%.2f%%\n", count, strategy, response.SpaceUtilization)
|
||
})
|
||
http.ListenAndServe(":8080", nil)
|
||
}
|
||
|
||
func optimizePacking(con Container, box Box) ([]Placement, string, int) {
|
||
fmt.Printf("📦 开始装箱优化\n容器尺寸:长%fmm × 宽%fmm × 高%fmm\n纸箱尺寸:长%fmm × 宽%fmm × 高%fmm\n",
|
||
con.Length, con.Width, con.Height, box.Length, box.Width, box.Height)
|
||
rotations := generateRotations(con, box)
|
||
fmt.Printf("🔧 生成有效旋转方式:共%d种\n", len(rotations))
|
||
type candidate struct {
|
||
layout []Placement
|
||
count int
|
||
strategy string
|
||
}
|
||
var candidates []candidate
|
||
for _, r := range rotations {
|
||
fmt.Printf("正在尝试旋转方式:%f×%f×%f\n", r.Length, r.Width, r.Height)
|
||
for _, strategy := range []string{"XY", "XZ", "YX", "YZ", "ZX", "ZY"} {
|
||
var xCount, yCount, zCount float64
|
||
switch strategy {
|
||
case "XY":
|
||
xCount = math.Floor(con.Length / r.Length)
|
||
yCount = math.Floor(con.Width / r.Width)
|
||
zCount = math.Floor(con.Height / r.Height)
|
||
case "XZ":
|
||
xCount = math.Floor(con.Length / r.Length)
|
||
zCount = math.Floor(con.Height / r.Height)
|
||
yCount = math.Floor(con.Width / r.Width)
|
||
case "YX":
|
||
yCount = math.Floor(con.Width / r.Length)
|
||
xCount = math.Floor(con.Length / r.Width)
|
||
zCount = math.Floor(con.Height / r.Height)
|
||
case "YZ":
|
||
yCount = math.Floor(con.Width / r.Length)
|
||
zCount = math.Floor(con.Height / r.Height)
|
||
xCount = math.Floor(con.Length / r.Width)
|
||
case "ZX":
|
||
zCount = math.Floor(con.Height / r.Length)
|
||
xCount = math.Floor(con.Length / r.Width)
|
||
yCount = math.Floor(con.Width / r.Height)
|
||
case "ZY":
|
||
zCount = math.Floor(con.Height / r.Length)
|
||
yCount = math.Floor(con.Width / r.Height)
|
||
xCount = math.Floor(con.Length / r.Width)
|
||
}
|
||
totalByVolume := int(xCount * yCount * zCount)
|
||
maxCountByWeight := int(math.Floor(con.WeightLimit / box.Weight))
|
||
actualCount := totalByVolume
|
||
if actualCount > maxCountByWeight {
|
||
actualCount = maxCountByWeight
|
||
}
|
||
if actualCount > 0 {
|
||
layout := generateLayout(r, xCount, yCount, zCount, strategy)
|
||
candidates = append(candidates, candidate{
|
||
layout: layout[:actualCount],
|
||
count: actualCount,
|
||
strategy: strategy,
|
||
})
|
||
fmt.Printf("💡 新增方案:策略[%s] 可装载%d箱(尺寸:%dx%dx%d)\n",
|
||
strategy, actualCount, xCount, yCount, zCount)
|
||
}
|
||
}
|
||
}
|
||
if len(candidates) == 0 {
|
||
fmt.Printf("❗️ 未找到有效装箱方案\n")
|
||
return nil, "", 0
|
||
}
|
||
maxCount := 0
|
||
var finalLayout []Placement
|
||
var finalStrategy string
|
||
for _, c := range candidates {
|
||
if c.count > maxCount {
|
||
maxCount = c.count
|
||
finalLayout = c.layout
|
||
finalStrategy = c.strategy
|
||
}
|
||
}
|
||
fmt.Printf("🏆 最优方案:策略[%s] 可装载%d箱\n", finalStrategy, maxCount)
|
||
return finalLayout, finalStrategy, maxCount
|
||
}
|
||
|
||
func generateRotations(con Container, box Box) []Box {
|
||
validRotations := make([]Box, 0)
|
||
rotations := []Box{
|
||
{Length: box.Length, Width: box.Width, Height: box.Height, Weight: box.Weight},
|
||
{Length: box.Length, Width: box.Height, Height: box.Width, Weight: box.Weight},
|
||
{Length: box.Width, Width: box.Length, Height: box.Height, Weight: box.Weight},
|
||
{Length: box.Width, Width: box.Height, Height: box.Length, Weight: box.Weight},
|
||
{Length: box.Height, Width: box.Length, Height: box.Width, Weight: box.Weight},
|
||
{Length: box.Height, Width: box.Width, Height: box.Length, Weight: box.Weight},
|
||
}
|
||
for _, r := range rotations {
|
||
if r.Length <= con.Length && r.Width <= con.Width && r.Height <= con.Height {
|
||
validRotations = append(validRotations, r)
|
||
}
|
||
}
|
||
return validRotations
|
||
}
|
||
|
||
func generateLayout(r Box, xCount, yCount, zCount float64, strategy string) []Placement {
|
||
fmt.Printf("正在生成布局:策略[%s](尺寸:%dx%dx%d)\n", strategy, xCount, yCount, zCount)
|
||
var layout []Placement
|
||
switch strategy {
|
||
case "XY":
|
||
for x := 0.0; x < xCount; x++ {
|
||
for y := 0.0; y < yCount; y++ {
|
||
for z := 0.0; z < zCount; z++ {
|
||
layout = append(layout, Placement{
|
||
X: x * r.Length,
|
||
Y: y * r.Width,
|
||
Z: z * r.Height,
|
||
RotationX: 0.0,
|
||
RotationY: 0.0,
|
||
RotationZ: 0.0,
|
||
BoxNumber: len(layout),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
case "XZ":
|
||
for x := 0.0; x < xCount; x++ {
|
||
for z := 0.0; z < zCount; z++ {
|
||
for y := 0.0; y < yCount; y++ {
|
||
layout = append(layout, Placement{
|
||
X: x * r.Length,
|
||
Y: y * r.Width,
|
||
Z: z * r.Height,
|
||
RotationX: 0.0,
|
||
RotationY: 0.0,
|
||
RotationZ: 0.0,
|
||
BoxNumber: len(layout),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
case "YX":
|
||
for y := 0.0; y < yCount; y++ {
|
||
for x := 0.0; x < xCount; x++ {
|
||
for z := 0.0; z < zCount; z++ {
|
||
layout = append(layout, Placement{
|
||
X: x * r.Width,
|
||
Y: y * r.Length,
|
||
Z: z * r.Height,
|
||
RotationX: 0.0,
|
||
RotationY: 0.0,
|
||
RotationZ: 0.0,
|
||
BoxNumber: len(layout),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
case "YZ":
|
||
for y := 0.0; y < yCount; y++ {
|
||
for z := 0.0; z < zCount; z++ {
|
||
for x := 0.0; x < xCount; x++ {
|
||
layout = append(layout, Placement{
|
||
X: x * r.Width,
|
||
Y: y * r.Length,
|
||
Z: z * r.Height,
|
||
RotationX: 0.0,
|
||
RotationY: 0.0,
|
||
RotationZ: 0.0,
|
||
BoxNumber: len(layout),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
case "ZX":
|
||
for z := 0.0; z < zCount; z++ {
|
||
for x := 0.0; x < xCount; x++ {
|
||
for y := 0.0; y < yCount; y++ {
|
||
layout = append(layout, Placement{
|
||
X: x * r.Width,
|
||
Y: y * r.Height,
|
||
Z: z * r.Length,
|
||
RotationX: 0.0,
|
||
RotationY: 0.0,
|
||
RotationZ: 0.0,
|
||
BoxNumber: len(layout),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
case "ZY":
|
||
for z := 0.0; z < zCount; z++ {
|
||
for y := 0.0; y < yCount; y++ {
|
||
for x := 0.0; x < xCount; x++ {
|
||
layout = append(layout, Placement{
|
||
X: x * r.Width,
|
||
Y: y * r.Height,
|
||
Z: z * r.Length,
|
||
RotationX: 0.0,
|
||
RotationY: 0.0,
|
||
RotationZ: 0.0,
|
||
BoxNumber: len(layout),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
default:
|
||
fmt.Printf("❗️ 无效策略:%s\n", strategy)
|
||
}
|
||
return layout
|
||
}
|
||
|
||
func calculateDensity(con Container, box Box, count int) float64 {
|
||
containerVolume := con.Length * con.Width * con.Height
|
||
boxVolume := float64(count) * box.Length * box.Width * box.Height
|
||
density := (boxVolume / containerVolume) * 100
|
||
fmt.Printf("📊 体积利用率计算:%.2f%%(箱子总容积%fmm³ / 集装箱容积%fmm³)\n",
|
||
density, boxVolume, containerVolume)
|
||
return density
|
||
}
|