Trae Assistant
feat: enhance assembly line tool with import/export and robust error handling
dda07d1
import os
import json
import math
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB Max Limit
@app.errorhandler(404)
def page_not_found(e):
return render_template('index.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return jsonify({"error": "Internal Server Error", "details": str(e)}), 500
@app.route('/api/upload', methods=['POST'])
def upload_file():
try:
if 'file' not in request.files:
return jsonify({"error": "No file part"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "No selected file"}), 400
if file:
try:
data = json.load(file)
return jsonify({"message": "File uploaded successfully", "data": data})
except json.JSONDecodeError:
return jsonify({"error": "Invalid JSON file"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
# Default Data (Enriched for User Experience)
DEFAULT_TASKS = [
{"id": "t1", "name": "安装底壳 (Install Chassis)", "duration": 15},
{"id": "t2", "name": "安装主板 (Mount PCB)", "duration": 25},
{"id": "t3", "name": "连接电源线 (Connect Power)", "duration": 10},
{"id": "t4", "name": "安装风扇 (Install Fan)", "duration": 12},
{"id": "t5", "name": "固定硬盘 (Secure HDD)", "duration": 18},
{"id": "t6", "name": "安装显卡 (Install GPU)", "duration": 20},
{"id": "t7", "name": "理线 (Cable Mgmt)", "duration": 30},
{"id": "t8", "name": "合盖 (Close Case)", "duration": 10},
{"id": "t9", "name": "贴标签 (Labeling)", "duration": 5},
{"id": "t10", "name": "最终测试 (Final Test)", "duration": 40}
]
DEFAULT_CONFIG = {
"work_hours": 8,
"target_output": 100 # Units per day
}
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/calculate', methods=['POST'])
def calculate_metrics():
"""
Calculate Line Efficiency, Smoothness Index, and Takt Time.
Input: { "stations": [[task1, task2], [task3]], "config": {...} }
"""
data = request.json
stations = data.get('stations', [])
config = data.get('config', DEFAULT_CONFIG)
# 1. Calculate Takt Time (Seconds)
# Takt Time = Available Time / Demand
available_seconds = config.get('work_hours', 8) * 3600
demand = config.get('target_output', 100)
if demand <= 0: demand = 1
takt_time = available_seconds / demand
# 2. Calculate Station Times
station_times = []
total_work_content = 0
for station in stations:
s_time = sum(t.get('duration', 0) for t in station)
station_times.append(s_time)
total_work_content += s_time
if not station_times:
return jsonify({"error": "No stations defined"})
# 3. Calculate Bottleneck (Max Station Time)
bottleneck_time = max(station_times)
bottleneck_index = station_times.index(bottleneck_time)
# 4. Calculate Efficiency
# Efficiency = Total Work Content / (Num Stations * Bottleneck Time)
num_stations = len(stations)
efficiency = 0
if num_stations > 0 and bottleneck_time > 0:
efficiency = (total_work_content / (num_stations * bottleneck_time)) * 100
# 5. Calculate Smoothness Index (SI)
# SI = Sqrt(Sum( (Max - Si)^2 ))
si_sum = sum((bottleneck_time - st)**2 for st in station_times)
smoothness_index = math.sqrt(si_sum)
# 6. Capacity Calculation
# Hourly Capacity = 3600 / Bottleneck Time
hourly_capacity = 0
if bottleneck_time > 0:
hourly_capacity = 3600 / bottleneck_time
daily_capacity = hourly_capacity * config.get('work_hours', 8)
return jsonify({
"metrics": {
"takt_time": round(takt_time, 2),
"bottleneck_time": round(bottleneck_time, 2),
"bottleneck_station": bottleneck_index + 1,
"line_efficiency": round(efficiency, 2),
"smoothness_index": round(smoothness_index, 2),
"hourly_capacity": round(hourly_capacity, 1),
"daily_capacity": round(daily_capacity, 1),
"total_work_content": total_work_content
},
"station_times": station_times
})
@app.route('/api/auto_balance', methods=['POST'])
def auto_balance():
"""
Simple Greedy Algorithm to re-balance line.
Try to fill stations up to Takt Time (or slightly above if unavoidable).
"""
data = request.json
all_tasks = data.get('tasks', [])
config = data.get('config', DEFAULT_CONFIG)
# Calculate Takt Time Target
available_seconds = config.get('work_hours', 8) * 3600
demand = config.get('target_output', 100)
takt_time = available_seconds / demand if demand > 0 else 60
# Heuristic: Sort tasks?
# In real assembly, order matters (precedence).
# Here we assume the Input List order IS the precedence constraint order (roughly).
# We will just cut the list into chunks.
new_stations = []
current_station = []
current_time = 0
for task in all_tasks:
t_dur = task.get('duration', 0)
# If adding this task exceeds Takt Time significantly, start new station?
# Let's try to fit as much as possible <= Takt Time.
# If a single task > Takt Time, it must be in a station alone (and will be bottleneck).
if current_time + t_dur <= takt_time:
current_station.append(task)
current_time += t_dur
else:
# If current station is not empty, close it
if current_station:
new_stations.append(current_station)
current_station = [task]
current_time = t_dur
else:
# Task itself is larger than Takt Time
new_stations.append([task])
current_time = 0 # Reset for next
current_station = []
if current_station:
new_stations.append(current_station)
return jsonify({
"stations": new_stations,
"message": "Auto-balance completed based on Takt Time constraint."
})
if __name__ == '__main__':
port = int(os.environ.get('PORT', 7860))
app.run(host='0.0.0.0', port=port, debug=True)