function [schedule_table, best_metrics] = GA_schedule()
clc;
% 初始化py库
obj = py.importlib.import_module(‘bj’);
py.importlib.reload(obj);
% 获取数据
orders_df = py.bj.fetch_orders();
production_lines_df = py.bj.fetch_production_lines();
% 转换函数
to_double = @(x) cellfun(@(v) double(py.float(v)), cell(py.list(x)), ‘UniformOutput’, false);
to_str = @(x) cellfun(@char, cell(py.list(x)), ‘UniformOutput’, false);
% 订单数据处理
order_ids = to_double(py.getattr(orders_df, ‘ORDER_ID’));
order_dates = datetime(to_str(py.getattr(orders_df, ‘ORDER_DATE’)));
due_dates = datetime(to_str(py.getattr(orders_df, ‘DUE_DATE’)));
product_ids = to_double(py.getattr(orders_df, ‘PRODUCT_ID’));
product_names = to_str(py.getattr(orders_df, ‘PRODUCT_NAME’));
product_spec = to_str(py.getattr(orders_df, ‘SPECIFICATION_INFO’));
order_quantities = to_double(py.getattr(orders_df, ‘ORDER_QUANTITY’));
outstanding_quantities = to_double(py.getattr(orders_df, ‘OUTSTANDING_QUANTITY’));
% 产线数据处理(LINE_ID 改为字符串)
line_names = to_str(py.getattr(production_lines_df, ‘LINE_NAME’));
line_ids = to_str(py.getattr(production_lines_df, ‘LINE_ID’));
line_rates = to_double(py.getattr(production_lines_df, ‘LINE_RATE’));
yield_rates = to_double(py.getattr(production_lines_df, ‘YIELD_RATE’));
status = to_str(py.getattr(production_lines_df, ‘STATUS’));
production_ids = to_double(py.getattr(production_lines_df, ‘PRODUCT_ID’));
% 构建订单结构体
num_orders = length(order_ids);
orders = struct([]);
for i = 1:num_orders
orders(i).OrderID = order_ids{i};
orders(i).OrderDate = order_dates(i);
orders(i).DueDate = due_dates(i);
orders(i).ProductID = product_ids{i};
orders(i).ProductName = product_names{i};
orders(i).Specification = product_spec{i};
orders(i).OrderQuantity = order_quantities{i};
orders(i).OutstandingQuantity = outstanding_quantities{i};
end
% 构建产线结构体
num_lines = length(line_ids);
productions = struct([]);
for j = 1:num_lines
productions(j).LineID = line_ids{j};
productions(j).LineName = line_names{j};
productions(j).LineRate = line_rates{j};
productions(j).YieldRate = yield_rates{j} / 100;
productions(j).Status = status{j};
productions(j).ProductID = production_ids{j};
end
% GA参数
ga_params.pop_size = 60;
ga_params.max_gen = 200;
ga_params.crossover_rate = 0.8;
ga_params.mutation_rate = 0.25;
ga_params.tournament_k = 3;
% 基准时间
all_order_dates = [orders.OrderDate];
baseline_start = min(all_order_dates);
line_id_to_index = containers.Map();
for j = 1:num_lines
line_id_to_index(productions(j).LineID) = j;
end
% 兼容产线:仍用索引存储
compatible_lines = cell(num_orders, 1);
for i = 1:num_orders
pid = orders(i).ProductID;
idxs = [];
for j = 1:num_lines
if isequal(productions(j).ProductID, pid) && strcmpi(productions(j).Status, ‘正常’)
idxs(end+1) = j;
end
end
compatible_lines{i} = idxs;
end
% 加工时间计算函数
calc_proc_duration = @(qty, rate, yield) hours(qty ./ (rate .* yield));
% 主模拟函数
function [schedule, metrics] = simulate_schedule(orders, productions, seq, assign, …
compatible_lines, calc_proc_duration, baseline_start, line_id_to_index)
num_orders_local = length(seq); num_productions = length(productions); line_available = repmat(baseline_start, num_productions, 1); schedule = repmat(struct(), num_orders_local, 1); total_late_hours = 0; latest_finish = baseline_start; penalty_unassigned = 0; for k = 1:num_orders_local i = seq(k); schedule(k).Seq = k; schedule(k).OrderID = orders(i).OrderID; schedule(k).OrderDate = orders(i).OrderDate; schedule(k).DueDate = orders(i).DueDate; schedule(k).ProductID = orders(i).ProductID; schedule(k).ProductName = orders(i).ProductName; schedule(k).Specification = orders(i).Specification; schedule(k).OrderQuantity = orders(i).OrderQuantity; schedule(k).OutstandingQuantity = orders(i).OutstandingQuantity; assigned_line_id = assign{i}; if isempty(assigned_line_id) || ~isKey(line_id_to_index, assigned_line_id) schedule(k).AssignedLineID = "待确认"; schedule(k).AssignedLineName = "待确认"; schedule(k).StartTime = NaT; schedule(k).EndTime = NaT; schedule(k).ProcDuration = duration(0,0,0); penalty_unassigned = penalty_unassigned + 1e6; continue; end j = line_id_to_index(assigned_line_id); % 转为索引 % 检查是否兼容 cl = compatible_lines{i}; if ~ismember(j, cl) schedule(k).AssignedLineID = string(assigned_line_id); schedule(k).AssignedLineName = productions(j).LineName; schedule(k).StartTime = NaT; schedule(k).EndTime = NaT; schedule(k).ProcDuration = duration(0,0,0); penalty_unassigned = penalty_unassigned + 1e5; continue; end qty = double(orders(i).OutstandingQuantity); rate = double(productions(j).LineRate); yld = double(productions(j).YieldRate); if rate <= 0 || yld <= 0 proc_dur = hours(1e6); else proc_dur = calc_proc_duration(qty, rate, yld); end est_start = max(line_available(j), orders(i).OrderDate); est_end = est_start + proc_dur; line_available(j) = est_end; schedule(k).AssignedLineID = string(assigned_line_id); schedule(k).AssignedLineName = string(productions(j).LineName); schedule(k).StartTime = est_start; schedule(k).EndTime = est_end; schedule(k).ProcDuration = proc_dur; late_h = max(0, hours(est_end - orders(i).DueDate)); total_late_hours = total_late_hours + late_h; if est_end > latest_finish latest_finish = est_end; end end metrics.total_late_hours = total_late_hours; metrics.latest_finish = latest_finish; metrics.penalty_unassigned = penalty_unassigned;
end
% 初始化种群
population(ga_params.pop_size) = struct();
for p = 1:ga_params.pop_size
seq = randperm(num_orders);
assign = cell(num_orders, 1);
for i = 1:num_orders cl = compatible_lines{i}; if ~isempty(cl) selected_idx = cl(randi(length(cl))); assign{i} = productions(selected_idx).LineID; % 存字符串 else assign{i} = ''; end end population(p).seq = seq; population(p).assign = assign; [~, metrics] = simulate_schedule(orders, productions, seq, assign, ... compatible_lines, calc_proc_duration, baseline_start, line_id_to_index); finish_hours = hours(metrics.latest_finish - baseline_start); population(p).fitness = metrics.total_late_hours + 0.0001*finish_hours + metrics.penalty_unassigned;
end
% 主循环
best_history = nan(ga_params.max_gen, 1);
for gen = 1:ga_params.max_gen
newpop = population;
[~, idxs_pop] = sort([population.fitness]);
newpop(1) = population(idxs_pop(1));
for slot = 2:ga_params.pop_size p1 = tournament_select(population, ga_params.tournament_k); p2 = tournament_select(population, ga_params.tournament_k); p1_assign = p1.assign; p2_assign = p2.assign; child = p1; if rand < ga_params.crossover_rate child.seq = order_crossover(p1.seq, p2.seq); child.assign = assign_crossover(p1_assign, p2_assign); end if rand < ga_params.mutation_rate child.seq = swap_mutation(child.seq); end if rand < ga_params.mutation_rate child.assign = assignment_mutation(child.assign, compatible_lines, productions, line_id_to_index); end [~, metrics] = simulate_schedule(orders, productions, child.seq, child.assign, ... compatible_lines, calc_proc_duration, baseline_start, line_id_to_index); finish_hours = hours(metrics.latest_finish - baseline_start); child.fitness = metrics.total_late_hours + 0.0001*finish_hours + metrics.penalty_unassigned; newpop(slot) = child; end population = newpop; best_history(gen) = min([population.fitness]);
end
% 提取最优解
[~, idxs] = sort([population.fitness]);
best_ind = population(idxs(1));
[best_schedule, best_metrics] = simulate_schedule(orders, productions, best_ind.seq, best_ind.assign, …
compatible_lines, calc_proc_duration, baseline_start, line_id_to_index);
% 转换为表格
n = length(best_schedule);
Seq = zeros(n,1);
OrderID = zeros(n,1);
OrderDate = NaT(n,1);
DueDate = NaT(n,1);
ProductID = zeros(n,1);
ProductName = strings(n,1);
Specification = strings(n,1);
OrderQuantity = zeros(n,1);
OutstandingQuantity = zeros(n,1);
AssignedLineID = strings(n,1);
AssignedLineName = strings(n,1);
StartTime = NaT(n,1);
EndTime = NaT(n,1);
ProcDuration = duration(zeros(n,1),0,0);
for k = 1:n
s = best_schedule(k);
Seq(k) = s.Seq;
OrderID(k) = s.OrderID;
OrderDate(k) = s.OrderDate;
DueDate(k) = s.DueDate;
ProductID(k) = s.ProductID;
ProductName(k) = string(s.ProductName);
Specification(k) = string(s.Specification);
OrderQuantity(k) = s.OrderQuantity;
OutstandingQuantity(k) = s.OutstandingQuantity;
AssignedLineID(k) = string(s.AssignedLineID);
AssignedLineName(k) = string(s.AssignedLineName);
StartTime(k) = s.StartTime;
EndTime(k) = s.EndTime;
ProcDuration(k) = s.ProcDuration;
end
% 分离有效与无效调度
valid_idx = ~isnat(StartTime);
invalid_idx = isnat(StartTime);
valid_indices = find(valid_idx);
invalid_indices = find(invalid_idx);
[~, sort_idx_valid] = sort(StartTime(valid_idx));
[~, sort_idx_invalid] = sort(OrderDate(invalid_idx));
final_idx = [valid_indices(sort_idx_valid); invalid_indices(sort_idx_invalid)];
% 重排
Seq = (1:n)';
OrderID = OrderID(final_idx);
OrderDate = OrderDate(final_idx);
DueDate = DueDate(final_idx);
ProductID = ProductID(final_idx);
ProductName = ProductName(final_idx);
Specification = Specification(final_idx);
OrderQuantity = OrderQuantity(final_idx);
OutstandingQuantity = OutstandingQuantity(final_idx);
AssignedLineID = AssignedLineID(final_idx);
AssignedLineName = AssignedLineName(final_idx);
StartTime = StartTime(final_idx);
EndTime = EndTime(final_idx);
ProcDuration = ProcDuration(final_idx);
% 输出表格
schedule_table = table(Seq, OrderID, OrderDate, DueDate, ProductID, ProductName, …
Specification, OrderQuantity, OutstandingQuantity, AssignedLineID, …
AssignedLineName, StartTime, EndTime, ProcDuration);
% 设置时间格式
schedule_table.StartTime.Format = ‘yyyy-MM-dd HH:mm:ss’;
schedule_table.EndTime.Format = ‘yyyy-MM-dd HH:mm:ss’;
schedule_table.OrderDate.Format = ‘yyyy-MM-dd HH:mm:ss’;
schedule_table.DueDate.Format = ‘yyyy-MM-dd HH:mm:ss’;
% 导出 Excel
if exist(‘schedule_table’, ‘var’)
chinese_headers = {‘序号’,‘订单号’,‘订单日期’,‘订单交期’,‘产品编号’,‘产品名称’,‘产品规格’,…
‘订单数目’,‘未交数目’,‘分配产线编号’,‘分配产线名称’,‘预定开始时间’,…
‘预定结束时间’,‘预期消耗时长’};
output_dir = fullfile(pwd, 'downloads'); if ~exist(output_dir, 'dir'), mkdir(output_dir); end output_path = fullfile(output_dir, '排程结果.xlsx'); % 保持列数一致 if width(schedule_table) ~= numel(chinese_headers) warning('列数不匹配,自动对齐'); end schedule_table.Properties.VariableNames = chinese_headers(1:width(schedule_table)); writetable(schedule_table, output_path, 'FileType', 'spreadsheet'); disp(['排程结果已导出:', output_path]);
else
warning(‘无法导出 Excel’);
end
% 辅助函数
function p = tournament_select(pop, k)
n = numel(pop);
idx = randi(n, [k,1]);
[~, bestidx] = min([pop(idx).fitness]);
p = pop(idx(bestidx));
end
function child_seq = order_crossover(a, b)
n = length(a);
p1 = sort(randi(n, [1,2]));
seg = a(p1(1):p1(2));
rest = b(~ismember(b, seg));
child_seq = [rest(1:p1(1)-1), seg, rest(p1(1):end)];
end
function child_assign = assign_crossover(a, b)
n = length(a);
cp = randi(n-1);
child_assign = [a(1:cp); b(cp+1:end)];
end
function s = swap_mutation(seq)
n = length(seq);
i = randi(n); j = randi(n);
tmp = seq(i); seq(i) = seq(j); seq(j) = tmp;
s = seq;
end
function asg = assignment_mutation(asg, compatible_lines, productions, line_id_to_index)
n = length(asg);
i = randi(n);
cl = compatible_lines{i};
if ~isempty(cl)
selected_idx = cl(randi(length(cl)));
asg{i} = productions(selected_idx).LineID;
else
asg{i} = ‘’;
end
end
end
这代码的排程能不能优化,大量数据计算太慢了