import os import random import simpy import numpy as np from flask import Flask, render_template, jsonify, request, send_from_directory app = Flask(__name__, template_folder='templates') # Configuration MAX_SIM_TIME = 480 # 8 hours in minutes for analysis SEED = 42 @app.route('/') def index(): return render_template('index.html') @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", message=str(e)), 500 @app.route('/api/upload_config', methods=['POST']) def upload_config(): 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: import json content = json.load(file) return jsonify({'message': 'Config uploaded successfully', 'config': content}) except Exception as e: return jsonify({'error': str(e)}), 500 class CallCenter: def __init__(self, env, num_employees, service_time_avg, service_time_std): self.env = env self.staff = simpy.Resource(env, num_employees) self.service_time_avg = service_time_avg self.service_time_std = service_time_std self.wait_times = [] self.service_times = [] self.utilization_log = [] self.events_trace = [] # For visualization: {'time': t, 'type': '...', 'id': ...} def support(self, customer): arrival_time = self.env.now self.events_trace.append({'time': arrival_time, 'type': 'arrival', 'id': customer}) with self.staff.request() as request: yield request wait = self.env.now - arrival_time self.wait_times.append(wait) self.events_trace.append({'time': self.env.now, 'type': 'start', 'id': customer, 'wait': wait}) # Service time (Normal distribution, clipped at 0.5 min) service_duration = max(0.5, random.gauss(self.service_time_avg, self.service_time_std)) yield self.env.timeout(service_duration) self.service_times.append(service_duration) self.events_trace.append({'time': self.env.now, 'type': 'finish', 'id': customer}) def customer_generator(env, center, arrival_rate): """ arrival_rate: Customers per hour """ i = 0 while True: # Inter-arrival time (Exponential distribution) yield env.timeout(random.expovariate(arrival_rate / 60.0)) i += 1 env.process(center.support(f'C{i}')) @app.route('/api/simulate', methods=['POST']) def simulate(): data = request.json # Parameters arrival_rate = float(data.get('arrival_rate', 60)) # Cust/hr service_time = float(data.get('service_time', 5)) # Avg min service_std = float(data.get('service_std', 1)) # Std Dev min num_servers = int(data.get('num_servers', 3)) duration = float(data.get('duration', 60)) # Minutes to simulate random.seed(SEED) env = simpy.Environment() center = CallCenter(env, num_servers, service_time, service_std) env.process(customer_generator(env, center, arrival_rate)) env.run(until=duration) # Calculate stats avg_wait = np.mean(center.wait_times) if center.wait_times else 0 max_wait = np.max(center.wait_times) if center.wait_times else 0 served_count = len(center.service_times) # Utilization estimate (Total Service Time / (Num Servers * Duration)) total_service = sum(center.service_times) utilization = (total_service / (num_servers * duration)) * 100 if num_servers > 0 else 0 return jsonify({ 'metrics': { 'avg_wait': round(avg_wait, 2), 'max_wait': round(max_wait, 2), 'served': served_count, 'utilization': round(utilization, 2) }, 'trace': center.events_trace }) @app.route('/api/optimize', methods=['POST']) def optimize(): data = request.json arrival_rate = float(data.get('arrival_rate', 60)) service_time = float(data.get('service_time', 5)) service_std = float(data.get('service_std', 1)) cost_per_server_hr = float(data.get('cost_server', 20)) # $20/hr cost_per_wait_hr = float(data.get('cost_wait', 50)) # $50/hr (Value of customer time/frustration) results = [] # Test 1 to 15 servers for n in range(1, 16): random.seed(SEED) # Reset seed for fair comparison env = simpy.Environment() center = CallCenter(env, n, service_time, service_std) env.process(customer_generator(env, center, arrival_rate)) env.run(until=480) # 8 hours avg_wait_min = np.mean(center.wait_times) if center.wait_times else 0 total_wait_hours = sum(center.wait_times) / 60.0 # Total Cost = (Server Cost * 8h) + (Total Wait Hours * Wait Cost) server_cost = n * cost_per_server_hr * 8 wait_cost = total_wait_hours * cost_per_wait_hr total_cost = server_cost + wait_cost utilization = (sum(center.service_times) / (n * 480)) * 100 results.append({ 'servers': n, 'total_cost': round(total_cost, 2), 'server_cost': round(server_cost, 2), 'wait_cost': round(wait_cost, 2), 'avg_wait': round(avg_wait_min, 2), 'utilization': round(utilization, 1) }) # Optimization: if cost starts increasing significantly and wait is low, we can stop, # but let's run all 15 for the chart. return jsonify({'results': results}) if __name__ == '__main__': app.run(host='0.0.0.0', port=7860, debug=True)