作业车间调度(Job shop scheduling problem, JSP) 是车间调度中最常见的调度类型,是最难的组合优化问题之一,应用领域极其广泛,涉及航母调度,机场飞机调度,港口码头货船调度,汽车加工流水线等,因此对其研究具有重大的现实意义。科学有效的生产调度不但可以提高生产加工过程中工人、设备资源的高效利用,还可缩短生产周期,降低生产成本。
一个加工系统有 M 台机器,要求加工 N 个作业,其中,作业 i 包含工序数为 。令,则 L 为任务集的总工序数。其中,各工序的加工时间已确定,并且每个作业必须按照工序的先后顺序加工。调度的任务是安排所有作业的加工调度排序,约束条件被满足的同时,使性能指标得到优化。作业车间调度需要考虑如下约束:
问题的数学模型:
令(i,j)表示作业i的第j个工序。 和 分别表示(i,j)的加工起始时刻和加工时间。 表示(i,j)是否在第k台机器上加工:如果(i,j)在第k台机器上加工, ;否则, , 为第k台机器的完工时间,则问题的数学模型如下:
公式(1)为目标函数,即优化目标,系统中使用总加工时间最短为优化目标。公式(2)表示1个作业只能在加工完成前一道工序后才可以加工后一道工序。公式(3)表示1个作业的第1道工序的起始加工时刻大于或等于0。公式(4)表示在1台机床上不会同时加工1个以上的作业。
随着遗传算法(genetic algorithm (GA))在组合优化问题的广泛应用,许多人开始对遗传算法进行深度研究。已有研究结果表明,遗传算法对求解作业车间调度问题具有较好的效果,因此系统采用遗传算法来解该问题,遗传算法是计算数学中用于解决最优化的搜索算法,是进化算法的一种。进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择以及杂交等。系统通过模拟生物进化,包括遗传、突变、选择等,来不断地产生新个体,并在算法终止时求得最优个体,即最优解。
遗传算法解决作业车间调度问题基本步骤
流程图如下
遗传算法所需参数
遗传算法实现基本步骤及伪代码
1. 编码及初始化种群
采用工序实数编码来表示染色体,即M台机器,N个工件,每个工件的工序数为 ,则染色体长度为 ,对染色体编码如下: 。其中 代表第i个工件编号,而出现的次数代表该工件的第几道工序。例如{0, 1, 2, 1, 2, 0, 0, 1, 2},中0,1,2表示工件的编号,第几次出现就代表第几道工序。然后将每一次随机生成的染色体个体加入到种群集合中。
2. 解码及计算适应度
将优化目标定义为总加工时间最短,因此适应度定义为最短加工时间的倒数,设fitness为对应个体的适应度,fulfillTime为最短加工时间,因此 其中fulfillTime的计算方法如下:
首先定义如下变量
然后从左到右遍历个体的染色体序列 ,其中 表示第i个工件的编号,则 对应的当前工序为 ,设为p。当前工件当前工序所使用的机器编号为 ,设为m。当前工件当前工序对应的加工时间为 ,设为t。则工件的第p道工序的最晚开始时间为
而第m台机器的加工时间为
工件 的第p道工序的结束时间为 最后加工完所有工件的最短加工时间fulfillTime为
从而计算出适应度fitness。
3. 个体选择算子
个体的选择使用锦标赛法,其基本策略为从整个种群中随机抽取n个个体让它们竞争,选取其中最优的个体。该算子的选择过程如下
4. 染色体交叉算子
使用Order Crossover(OX)交叉算子,该算子的交叉步骤如下:
对于一对染色体g1, g2,首先随机产生一个起始位置start和终止位置end,并由从g1的染色体序列从start到end的序列中产生一个子代原型
将g2中不包含在child prototype的其余编码加入到child prototype两侧
上述步骤将产生一个child,交换g1, g2即可产生另一个child
5. 染色体变异算子
变异的作用主要是使算法能跳出局部最优解,因此不同的变异方式对算法能否求得全局最优解有很大的影响。使用位置变异法作为变异算子,即从染色体中随机产生两个位置并交换这两个位置的值
遗传算法代码实现
根据上面的步骤及伪代码,很容易就能写出Python及Java对应的代码实现了(C++实现在这里: 遗传算法与作业车间调度C++实现 ),如下:
Python代码:
from random import (randint) from typing import (List, Tuple, Set, Dict, Any) from utils.utils import reshape_data from collections import namedtuple MATRIX_SIZE = 500 # 个体对象,染色体和适应度 class Gene(object): def __init__(self, fitness: float = 0, chromosome = None): self.fitness = fitness self.chromosome: list = chromosome def __eq__(self, other): if isinstance(other, Gene): return other.fitness == self.fitness and other.chromosome == self.chromosome return False def __hash__(self): return hash("".join(map(lambda x: str(x), self.chromosome))) def __str__(self): return "{} => {}".format(self.chromosome, self.fitness) # 存储解码结果 class GeneEvaluation: def __init__(self): self.fulfill_time = 0 self.machine_work_time = [0 for _ in range(MATRIX_SIZE)] self.process_ids = [0 for _ in range(MATRIX_SIZE)] self.end_time = [[0 for _ in range(MATRIX_SIZE)] for _ in range(MATRIX_SIZE)] self.start_time = [[0 for _ in range(MATRIX_SIZE)] for _ in range(MATRIX_SIZE)] # 遗传算法实现 class GA: def __init__(self, population_number = 50, times = 10, cross_probability = 0.95, mutation_probability = 0.05, workpiece_number = 0, machine_number = 0): self.population_number = population_number # 种群数量 self.times = times # 遗传代数 self.cross_probability = cross_probability # 交叉概率 self.mutation_probability = mutation_probability # 突变概率 self.workpiece_number = workpiece_number # 工件数量 self.machine_number = machine_number # 机器数量 self.process_number: int = 0 # 工序数量 self.chromosome_size: int = 0 # 染色体长度 self.machine_matrix = [[-1 for _ in range(MATRIX_SIZE)] for _ in range(MATRIX_SIZE)] self.time_matrix = [[-1 for _ in range(MATRIX_SIZE)] for _ in range(MATRIX_SIZE)] self.process_matrix = [[-1 for _ in range(MATRIX_SIZE)] for _ in range(MATRIX_SIZE)] self.genes: Set[Gene] = set() # 评估染色体 def evaluate_gene(self, g: Gene) -> GeneEvaluation: evaluation = GeneEvaluation() # print(g.chromosome) for workpiece_id in g.chromosome: process_id = evaluation.process_ids[workpiece_id] machine_id = self.machine_matrix[workpiece_id][process_id] time = self.time_matrix[workpiece_id][process_id] evaluation.process_ids[workpiece_id] += 1 evaluation.start_time[workpiece_id][process_id] = evaluation.machine_work_time[machine_id] / if process_id == 0 else max(evaluation.end_time[workpiece_id][process_id - 1], evaluation.machine_work_time[machine_id]) evaluation.machine_work_time[machine_id] = evaluation.start_time[workpiece_id][process_id] + time evaluation.end_time[workpiece_id][process_id] = evaluation.machine_work_time[machine_id] evaluation.fulfill_time = max(evaluation.fulfill_time, evaluation.machine_work_time[machine_id]) return evaluation # 计算适应度 def calculate_fitness(self, g: Gene) -> float: return 1 / self.evaluate_gene(g).fulfill_time # 个体交叉 def gene_cross(self, g1: Gene, g2: Gene) -> tuple: chromosome_size = self.chromosome_size def gene_generate(father: Gene, mother: Gene) -> Gene: index_list = list(range(chromosome_size)) p1 = index_list.pop(randint(0, len(index_list) - 1)) p2 = index_list.pop(randint(0, len(index_list) - 1)) start = min(p1, p2) end = max(p1, p2) prototype = father.chromosome[start: end + 1] t = mother.chromosome[0:] for v1 in prototype: for i in range(len(t)): if v1 == t[i]: t.pop(i) break child = Gene() child.chromosome = t[0: start] + prototype + t[start:] child.fitness = self.calculate_fitness(child) return child return gene_generate(g1, g2), gene_generate(g2, g1) # 突变 def gene_mutation(self, g: Gene, n = 2) -> None: index_list = [i for i in range(self.chromosome_size)] for i in range(n): a = index_list.pop(randint(0, len(index_list) - 1)) b = index_list.pop(randint(0, len(index_list) - 1)) g.chromosome[a], g.chromosome[b] = g.chromosome[b], g.chromosome[a] g.fitness = self.calculate_fitness(g) # 初始化种群 [0, 1, 2, 1, 2, 0, 0, 1] => 12 def init_population(self): for _ in range(self.population_number): g = Gene() size = self.workpiece_number * self.machine_number # print(self.workpiece_number, self.machine_number) index_list = list(range(size)) chromosome = [-1 for _ in range(size)] for j in range(self.workpiece_number): for k in range(self.machine_number): index = randint(0, len(index_list) - 1) val = index_list.pop(index) if self.process_matrix[j][k] != -1: chromosome[val] = j g.chromosome = list(filter(lambda x: x != -1, chromosome)) # print("chromosome:", g.chromosome) g.fitness = self.calculate_fitness(g) self.genes.add(g) # 选择个体,锦标赛法 def select_gene(self, n: int = 3): if len(self.genes) <= 3: best_gene = Gene(0) for g in self.genes: if g.fitness > best_gene.fitness: best_gene = g return best_gene index_list = list(range(len(self.genes))) index_set = {index_list.pop(randint(0, len(index_list) - 1)) for _ in range(n)} best_gene = Gene(0) i = 0 for gene in self.genes: if i in index_set: if best_gene.fitness < gene.fitness: best_gene = gene i += 1 return best_gene # 遗传算法 def exec(self, parameter: List[List[Tuple]]) -> GeneEvaluation: # print(parameter) workpiece_size = len(parameter) for i in range(workpiece_size): self.chromosome_size += len(parameter[i]) self.process_number = max(self.process_number, len(parameter[i])) for j in range(len(parameter[i])): self.machine_matrix[i][j] = parameter[i][j][0] self.time_matrix[i][j] = parameter[i][j][1] for i in range(workpiece_size): for j in range(self.process_number): if self.machine_matrix[i][j] != -1: self.process_matrix[i][self.machine_matrix[i][j]] = j self.init_population() for _ in range(self.times): probability = randint(1, 100) / 100 if probability < self.mutation_probability: index = randint(0, len(self.genes)) i = 0 for gene in self.genes: if i == index: self.gene_mutation(gene) break i += 1 else: g1, g2 = self.select_gene(), self.select_gene() children = self.gene_cross(g1, g2) self.genes.update({*children}) best_gene = Gene(0) for gene in self.genes: if best_gene.fitness < gene.fitness: best_gene = gene return self.evaluate_gene(best_gene) ResultData = namedtuple("ResultData", ["fulfill_time", "row_data", "json_data"]) # 输出结果 def schedule(data) -> ResultData: print(data) reshape = reshape_data(data) parameter = reshape.result print(parameter) n = len(reshape.workpiece) m = len(reshape.machine) # number from 0 print(m) ga = GA(workpiece_number = n, machine_number = m) result = ga.exec(parameter) p = ga.process_number machine_matrix = ga.machine_matrix row_data = [] for i in range(n): for j in range(p): if machine_matrix[i][j] != -1: temp = { "workpiece": reshape.workpiece[i], "process": reshape.process[i][j], "machine": reshape.machine[machine_matrix[i][j]], "startTime": result.start_time[i][j], "endTime": result.end_time[i][j] } # print(i, j, machine_matrix[i][j], result.start_time[i][j], result.end_time[i][j]) row_data.append(temp) json_data = {} for i in range(n): for j in range(p): if machine_matrix[i][j] != -1: temp = { "workpiece": reshape.workpiece[i], "process": reshape.process[i][j], "startTime": result.start_time[i][j], "endTime": result.end_time[i][j] } m = reshape.machine[machine_matrix[i][j]] if m not in json_data: json_data[m] = [temp] else: json_data[m].append(temp) return ResultData(result.fulfill_time, row_data, json_data) if __name__ == "__main__": # 测试数据 d = [{'workpiece': '#W-89-10', 'process': '#P-1349-31', 'machine': '#M-8763-12', 'time': 10, 'order': 0}, {'workpiece': '#W-89-10', 'process': '#P-6261-32', 'machine': '#M-2304-14', 'time': 21, 'order': 1}, {'workpiece': '#W-89-10', 'process': '#P-6917-33', 'machine': '#M-6360-16', 'time': 12, 'order': 2}, {'workpiece': '#W-5863-13', 'process': '#P-2772-34', 'machine': '#M-6557-17', 'time': 21, 'order': 0}, {'workpiece': '#W-5863-13', 'process': '#P-468-35', 'machine': '#M-8763-12', 'time': 21, 'order': 1}, {'workpiece': '#W-5829-8', 'process': '#P-3959-28', 'machine': '#M-2304-14', 'time': 5, 'order': 2}, {'workpiece': '#W-5829-8', 'process': '#P-5852-27', 'machine': '#M-671-13', 'time': 11, 'order': 1}, {'workpiece': '#W-5829-8', 'process': '#P-7792-26', 'machine': '#M-8763-12', 'time': 10, 'order': 0}, {'workpiece': '#W-554-9', 'process': '#P-6810-29', 'machine': '#M-671-13', 'time': 5, 'order': 0}] print(schedule(d).row_data) 复制代码
工具reshape_data函数实现
from typing import (List, Dict) from collections import namedtuple test_data = [{"workpiece": '#W-5829-8', "process": '#P-3959-28', "machine": '#M-2304-14', "time": 5, "order": 2}, {"workpiece": '#W-5829-8', "process": '#P-5852-27', "machine": '#M-671-13', "time": 11, "order": 1}, {"workpiece": '#W-5829-8', "process": '#P-7792-26', "machine": '#M-8763-12', "time": 10, "order": 0}, {"workpiece": '#W-554-9', "process": '#P-6810-29', "machine": '#M-671-13', "time": 5, "order": 0}, {"workpiece": '#W-554-9', "process": '#P-8883-30', "machine": '#M-3836-15', "time": 10, "order": 1}] ReshapeData = namedtuple("ReshapeData", ["result", "workpiece", "machine", "process", "reverse_workpiece", "reverse_machine"]) def make_reverse_index(arr: list) -> dict: result = {} for i in range(len(arr)): result[arr[i]] = i return result def filter_value(origin: list, except_value: int) -> list: return list(filter(lambda v: v != except_value, origin)) def reshape_data(data: List[Dict]) -> ReshapeData: def make_array(r: dict) -> ReshapeData: workpieces = list(set(map(lambda v: v["workpiece"], data))) machines = list(set(map(lambda v: v["machine"], data))) process = [-1 for _ in workpieces] reverse_workpieces = make_reverse_index(workpieces) reverse_machines = make_reverse_index(machines) ans = [-1 for _ in r.keys()] for key, val in r.items(): # print(val, type(val)) m = max(*map(lambda v: v["order"], val)) + 1 if len(val) > 1 else val[0]["order"] t = [-1 for _ in range(m + 1)] x = [-1 for _ in range(m + 1)] for p in val: t[p["order"]] = (reverse_machines[p["machine"]], p["time"]) x[p["order"]] = p["process"] x = filter_value(x, -1) t = filter_value(t, -1) if ans[reverse_workpieces[key]] == -1: ans[reverse_workpieces[key]] = t else: ans[reverse_workpieces[key]].append(t) process[reverse_workpieces[key]] = x process = filter_value(process, -1) ans = filter_value(ans, -1) return ReshapeData(ans, workpieces, machines, process, reverse_machines, reverse_workpieces) result = {} for value in data: w = value["workpiece"] if w in result: result[w].append(value) else: result[w] = [value] # print(result) return make_array(result) if __name__ == "__main__": print(reshape_data(test_data).result) print(reshape_data(test_data).machine) 复制代码
同理Java也很容易实现:
import java.util.*; class GeneticAlgorithm { private final int populationNumber = 60; private final double crossProbability = 0.95; private final double mutationProbability = 0.05; private int jobNumber; private int machineNumber; private int processNumber; private int chromosomeSize; private int[][] machineMatrix = new int[1024][1024]; private int[][] timeMatrix = new int[1024][1024]; private int[][] processMatrix = new int[1024][1024]; private Set<Gene> geneSet = new HashSet<>(); private Random random = new Random(); public GeneticAlgorithm(int jobNumber, int machineNumber) { this.jobNumber = jobNumber; this.machineNumber = machineNumber; for (int[] matrix : this.machineMatrix) Arrays.fill(matrix, -1); for (int[] process : this.processMatrix) Arrays.fill(process, -1); } private List<Integer> makeList(int n) { List<Integer> result = new ArrayList<>(); for (int i = 0; i < n; i++) result.add(i); return result; } private Integer[] filterArray(Integer[] arr, int filterVal) { List<Integer> result = new ArrayList<>(); for (int i = 0; i < arr.length; i++) { if (arr[i] != filterVal) { result.add(arr[i]); } } return result.toArray(new Integer[0]); } // 初始化种群 public void initialPopulation() { for (int i = 0; i < populationNumber; i ++) { Gene g = new Gene(); int size = jobNumber * machineNumber; List<Integer> indexList = makeList(size); Integer[] chromosome = new Integer[size]; Arrays.fill(chromosome, -1); for (int j = 0; j < jobNumber; j++) { for (int k = 0; k < machineNumber; k ++) { int index = random.nextInt(indexList.size()); int val = indexList.remove(index); if (processMatrix[j][k] != -1) { chromosome[val] = j; } } } g.chromosome = filterArray(chromosome, -1); g.fitness = calculateFitness(g).fulfillTime; geneSet.add(g); } } public List<Integer> subArray(Integer[] arr, int start, int end) { List<Integer> list = new ArrayList<>(); for (int i = start; i < end; i++) list.add(arr[i]); return list; } // 计算适应度 public Result calculateFitness(Gene g) { Result result = new Result(); for (int i = 0; i < g.chromosome.length; i ++) { int jobId = g.chromosome[i]; int processId = result.processIds[jobId]; int machineId = machineMatrix[jobId][processId]; int time = timeMatrix[jobId][processId]; result.processIds[jobId] += 1; result.startTime[jobId][processId] = processId ==0 ? result.machineWorkTime[machineId] : Math.max(result.endTime[jobId][processId - 1], result.machineWorkTime[machineId]); result.machineWorkTime[machineId] = result.startTime[jobId][processId] + time; result.endTime[jobId][processId] = result.machineWorkTime[machineId]; result.fulfillTime = Math.max(result.fulfillTime, result.machineWorkTime[machineId]); } return result; } // 交叉算子 private Gene crossGene(Gene g1, Gene g2) { List<Integer> indexList = makeList(chromosomeSize); int p1 = indexList.remove(random.nextInt(indexList.size())); int p2 = indexList.remove(random.nextInt(indexList.size())); int start = Math.min(p1, p2); int end = Math.max(p1, p2); List<Integer> proto = subArray(g1.chromosome, start, end + 1); List<Integer> t = new ArrayList<>(); for (Integer c : g2.chromosome) t.add(c); for (Integer val : proto) { for (int i = 0; i < t.size(); i++) { if (val.equals(t.get(i))) { t.remove(i); break; } } } Gene child = new Gene(); proto.addAll(t.subList(start, t.size())); List<Integer> temp = t.subList(0, start); temp.addAll(proto); child.chromosome = temp.toArray(new Integer[0]); child.fitness = (double) calculateFitness(child).fulfillTime; return child; } // 突变算子 public Gene mutationGene(Gene gene, int n) { List<Integer> indexList = makeList(chromosomeSize); for (int i = 0; i < n; i++) { int a = indexList.remove(random.nextInt(indexList.size())); int b = indexList.remove(random.nextInt(indexList.size())); int t = gene.chromosome[a]; gene.chromosome[a] = gene.chromosome[b]; gene.chromosome[b] = t; } gene.fitness = calculateFitness(gene).fulfillTime; return gene; } // 选择个体 public Gene selectGene(int n) { List<Integer> indexList = makeList(geneSet.size()); Map<Integer, Boolean> map = new HashMap<>(); for (int i = 0; i < n; i++) { map.put(indexList.remove(random.nextInt(indexList.size())), true); } Gene bestGene = new Gene(0xfffff); int i = 0; for (Gene gene : geneSet) { if (map.containsKey(i)) { if (bestGene.fitness > gene.fitness) { bestGene = gene; } } i ++; } return bestGene; } public Result run(List<List<Integer[]>> job) { int jobSize = job.size(); for (int i = 0; i < jobSize; i ++) { chromosomeSize += job.get(i).size(); processNumber = Math.max(processNumber, job.get(i).size()); for (int j = 0; j < job.get(i).size(); j ++) { machineMatrix[i][j] = job.get(i).get(j)[0]; timeMatrix[i][j] = job.get(i).get(j)[1]; } } for (int i = 0; i < jobSize; i++) { for (int j = 0;j < processNumber; j++){ if (machineMatrix[i][j] != -1) { processMatrix[i][machineMatrix[i][j]] = j; } } } initialPopulation(); for (int i = 0; i < populationNumber; i++) { double p = (double) random.nextInt(100) / 100.0; if (p < mutationProbability) { int index = random.nextInt(geneSet.size()); int k = 0; for (Gene gene : geneSet) { if (k == index) { mutationGene(gene); break; } k ++; } } else { Gene g1 = selectGene(), g2 = selectGene(); Gene child1 = crossGene(g1, g2), child2 = crossGene(g2, g1); geneSet.add(child1); geneSet.add(child2); } } Gene bestGene = new Gene(0xffffff); for (Gene gene : geneSet) { if (bestGene.fitness > gene.fitness) { bestGene = gene; } } return calculateFitness(bestGene); } public Gene selectGene() { return selectGene(3); } public Gene mutationGene(Gene gene) { return mutationGene(gene, 2); } static public void main(String[] args) { List<List<Integer[]>> job = Arrays.asList( Arrays.asList(new Integer[]{0, 3}, new Integer[]{1, 2}, new Integer[]{2, 2}), Arrays.asList(new Integer[]{0, 2}, new Integer[]{2, 1}, new Integer[]{1, 4}), Arrays.asList(new Integer[]{1, 4}, new Integer[]{2, 3}) ); int n = 3, m = 3; GeneticAlgorithm ga = new GeneticAlgorithm(n, m); Result result = ga.run(job); int processNumber = ga.processNumber; int[][] machineMatrix = ga.machineMatrix; System.out.println(result.fulfillTime); for (int i = 0; i < n; i++) { for (int j = 0 ; j < processNumber; j++) { if (machineMatrix[i][j] != -1) { System.out.println(String.format("job: %d, process: %d, machine: %d, startTime: %d, endTime: %d", i, j, machineMatrix[i][j], result.startTime[i][j], result.endTime[i][j])); } } } } } class Gene { public double fitness; public Integer[] chromosome; public Gene() { fitness = 0; } public Gene(double fitness) {this.fitness = fitness;} } class Result { public int fulfillTime = 0; public int[] machineWorkTime = new int[1024]; public int[] processIds = new int[1024]; public int[][] endTime = new int[1024][1024]; public int[][] startTime = new int[1024][1024]; }复制代码
写了那么多的遗传算法解作业车间调度问题,当然还要有实际应用了,因此开发了一个作业车间调度系统,核心功能很简单就是对工件、机器、工序进行增删改查并使用遗传算法计算调度结果,前端用甘特图展示调度结果。(GitHub地址: github.com/sundial-dre… ,如果觉得项目不错,别忘了点赞哦)
系统总体技术路线:采用前后端分离模式开发,基于C/S模式,客户端使用JavaScript,Electron, React,Node.js等进行组件化开发,服务端使用express,nodejs,typescript,python,sanic,graphql等开发独立的graphql服务或http服务,数据库使用mysql来存储基本的信息,redis来实现id生成,mongodb来存储调度结果。
Client端: 使用JavaScript来开发PC端应用程序。基于Electron进行开发,在Electron基础上使用React、Redux、Antd、Echarts等前端技术和包,以模块化、组件化的方式构建一个独立的UI页面,对于页面的切换使用了前端路由React-router。除此之外,通过封装一个GraphQL类来做GraphQL查询,以此和后端进行数据交互。通过这种方式能快速的构建一个可维护性好,UI美观的PC端应用程序。
GrapgQL Service端: 使用Typescript开发的一个GraphQL服务,提供GraphQL查询。基于NodeJS、Express、GraphQL等来构建一个GraphQL服务器,由于Node.js的异步非阻塞特性,使得构建的服务器并发能力更强。除此之外,使用TypeORM来做数据库的对象关系映射,加快开发速度。通过这种方式能快速搭建起一个GraphQL服务端,使用GraphQL查询来替代传统的RESTful API能使提供的API服务可维护性、扩展性更强。
Schedule Service端: 使用Python开发,提供作业调度服务。基于Python异步Web框架Sanic,使得构建的服务器运行效率和并发能力都比较强,并且使用Python来实现作业车间调度算法(遗传算法)一方面是比较容易,另一方面是Python支持多线程编程,因此多线程来优化算法也能实现。
Data Storage端: 数据存储,使用MySQL来存储一些基本的数据,如机器信息、工件信息、工件工艺信息、员工信息等等。使用Redis来存储一些健值对信息以及Id生成,由于Redis的单线程异步非阻塞特性,使得生成的Id不存在重复。使用MongoDB来存储调度结果,由于调度结果完全是JSON格式数据,与使用MySQL存储相比,使用文档数据库MongoDB来存储比较容易,而且查询也比较方便。
项目运行:
首页
管理
作业调度
最后,如果觉得这篇文章有帮助,别忘了点赞哦