1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,90 @@ |
0 |
+import { |
|
1 |
+ addIndex, |
|
2 |
+ findIndex, |
|
3 |
+ find, |
|
4 |
+ map, |
|
5 |
+ propEq, |
|
6 |
+ reduce, |
|
7 |
+ sort, |
|
8 |
+ range, |
|
9 |
+ concat, |
|
10 |
+ filter, |
|
11 |
+ reverse, |
|
12 |
+ prop, |
|
13 |
+ flatten, |
|
14 |
+ uniq, |
|
15 |
+ reject, |
|
16 |
+ contains, |
|
17 |
+ omit, |
|
18 |
+ equals, |
|
19 |
+ update, |
|
20 |
+ times, |
|
21 |
+ add, |
|
22 |
+ pluck, |
|
23 |
+ last |
|
24 |
+} from 'rambda'; |
|
25 |
+import type { JobId } from '../../types'; |
|
26 |
+import { max, min, remove, slice } from 'ramda'; |
|
27 |
+import { createSchedule as johnson, updateCTime } from '../johnson'; |
|
28 |
+ |
|
29 |
+type JobOperations = { |
|
30 |
+ jobId: JobId, |
|
31 |
+ operations: Array<{ t: number }> |
|
32 |
+}; |
|
33 |
+ |
|
34 |
+type JobOperationsWithC = { |
|
35 |
+ jobId: JobId, |
|
36 |
+ operations: Array<{ t: number, c: number }> |
|
37 |
+}; |
|
38 |
+ |
|
39 |
+export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => { |
|
40 |
+ const allSchedules = map(k => { |
|
41 |
+ const jobsOperationsForK = map((jobsOperation: JobOperations) => { |
|
42 |
+ const t1 = reduce((acc, i) => acc + jobsOperation.operations[i].t, 0, range(0, k)); |
|
43 |
+ const t2 = reduce((acc, i) => acc + jobsOperation.operations[i].t, 0, range(processorsCount - k, processorsCount)); |
|
44 |
+ |
|
45 |
+ const operations = [ |
|
46 |
+ { t: t1 }, |
|
47 |
+ { t: t2 } |
|
48 |
+ ]; |
|
49 |
+ const newJobsOperations = { jobId: jobsOperation.jobId, operations }; |
|
50 |
+ return newJobsOperations; |
|
51 |
+ }, jobsOperations); |
|
52 |
+ |
|
53 |
+ const jobsOperationsOrderTemplate = johnson(jobsOperationsForK); |
|
54 |
+ |
|
55 |
+ const mapWithIndex = addIndex(map); |
|
56 |
+ const jobsOperationsReordered = mapWithIndex((jobOperationTemplate, i) => { |
|
57 |
+ const { operations } = find(propEq('jobId', jobOperationTemplate.jobId), jobsOperations); |
|
58 |
+ return { |
|
59 |
+ jobId: jobOperationTemplate.jobId, |
|
60 |
+ operations |
|
61 |
+ }; |
|
62 |
+ }, jobsOperationsOrderTemplate); |
|
63 |
+ |
|
64 |
+ const jobsOperationsReorderedWithC = updateCTime(jobsOperationsReordered, processorsCount); |
|
65 |
+ |
|
66 |
+ return jobsOperationsReorderedWithC; |
|
67 |
+ }, range(1, processorsCount)); |
|
68 |
+ |
|
69 |
+ const bestSchedule = reduce((acc, schedule) => { |
|
70 |
+ const cmaxAcc = last(last(acc).operations).c; |
|
71 |
+ const cmax = last(last(schedule).operations).c; |
|
72 |
+ |
|
73 |
+ return (cmax < cmaxAcc) ? schedule : acc; |
|
74 |
+ }, [{ operations: [{ c: Infinity }] }], allSchedules); |
|
75 |
+ |
|
76 |
+ return bestSchedule; |
|
77 |
+}; |
|
78 |
+ |
|
79 |
+export default (jobsOperations: JobOperations[], processorsCount: number) => { |
|
80 |
+ const schedule = createSchedule(jobsOperations, processorsCount); |
|
81 |
+ |
|
82 |
+ return times(i => |
|
83 |
+ map((jobTime: JobOperationsWithC) => ({ |
|
84 |
+ processor: i + 1, |
|
85 |
+ startTime: jobTime.operations[i].c - jobTime.operations[i].t, |
|
86 |
+ endTime: jobTime.operations[i].c, |
|
87 |
+ }), schedule) |
|
88 |
+ , processorsCount); |
|
89 |
+}; |
|
0 | 90 |
\ No newline at end of file |
1 | 91 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,73 @@ |
0 |
+import createGantt, { createSchedule } from './index'; |
|
1 |
+ |
|
2 |
+test('create schedule', () => { |
|
3 |
+ const jobsWithOperations = [ |
|
4 |
+ { jobId: 1, operations: [ { t: 12 }, { t: 8 }, { t: 11 } ] }, |
|
5 |
+ { jobId: 2, operations: [ { t: 6 }, { t: 13 }, { t: 19 } ] }, |
|
6 |
+ { jobId: 3, operations: [ { t: 15 }, { t: 10 }, { t: 18 } ] }, |
|
7 |
+ { jobId: 4, operations: [ { t: 16 }, { t: 7 }, { t: 21 } ] }, |
|
8 |
+ { jobId: 5, operations: [ { t: 4 }, { t: 4 }, { t: 20 } ] }, |
|
9 |
+ { jobId: 6, operations: [ { t: 3 }, { t: 12 }, { t: 13 } ] }, |
|
10 |
+ { jobId: 7, operations: [ { t: 17 }, { t: 6 }, { t: 8 } ] }, |
|
11 |
+ { jobId: 8, operations: [ { t: 10 }, { t: 2 }, { t: 25 } ] }, |
|
12 |
+ ]; |
|
13 |
+ |
|
14 |
+ const schedule = createSchedule(jobsWithOperations, 3); |
|
15 |
+ |
|
16 |
+ expect(schedule.length).toBe(8); |
|
17 |
+ expect(schedule[0]).toEqual({ jobId: 5, operations: [ { t: 4, c: 4 }, { t: 4, c: 8 }, { t: 20, c: 28 } ] }); |
|
18 |
+ expect(schedule[1]).toEqual({ jobId: 8, operations: [ { t: 10, c: 14 }, { t: 2, c: 16 }, { t: 25, c: 53 } ] }); |
|
19 |
+ expect(schedule[2]).toEqual({ jobId: 6, operations: [ { t: 3, c: 17 }, { t: 12, c: 29 }, { t: 13, c: 66 } ] }); |
|
20 |
+ expect(schedule[3]).toEqual({ jobId: 2, operations: [ { t: 6, c: 23 }, { t: 13, c: 42 }, { t: 19, c: 85 } ] }); |
|
21 |
+ expect(schedule[4]).toEqual({ jobId: 4, operations: [ { t: 16, c: 39 }, { t: 7, c: 49 }, { t: 21, c: 106 } ] }); |
|
22 |
+ expect(schedule[5]).toEqual({ jobId: 3, operations: [ { t: 15, c: 54 }, { t: 10, c: 64 }, { t: 18, c: 124 } ] }); |
|
23 |
+ expect(schedule[6]).toEqual({ jobId: 1, operations: [ { t: 12, c: 66 }, { t: 8, c: 74 }, { t: 11, c: 135 } ] }); |
|
24 |
+ expect(schedule[7]).toEqual({ jobId: 7, operations: [ { t: 17, c: 83 }, { t: 6, c: 89 }, { t: 8, c: 143 } ] }); |
|
25 |
+}); |
|
26 |
+ |
|
27 |
+test('create gantt', () => { |
|
28 |
+ const jobsWithOperations = [ |
|
29 |
+ { jobId: 1, operations: [ { t: 12 }, { t: 8 }, { t: 11 } ] }, |
|
30 |
+ { jobId: 2, operations: [ { t: 6 }, { t: 13 }, { t: 19 } ] }, |
|
31 |
+ { jobId: 3, operations: [ { t: 15 }, { t: 10 }, { t: 18 } ] }, |
|
32 |
+ { jobId: 4, operations: [ { t: 16 }, { t: 7 }, { t: 21 } ] }, |
|
33 |
+ { jobId: 5, operations: [ { t: 4 }, { t: 4 }, { t: 20 } ] }, |
|
34 |
+ { jobId: 6, operations: [ { t: 3 }, { t: 12 }, { t: 13 } ] }, |
|
35 |
+ { jobId: 7, operations: [ { t: 17 }, { t: 6 }, { t: 8 } ] }, |
|
36 |
+ { jobId: 8, operations: [ { t: 10 }, { t: 2 }, { t: 25 } ] }, |
|
37 |
+ ]; |
|
38 |
+ |
|
39 |
+ const template = createGantt(jobsWithOperations, 3); |
|
40 |
+ |
|
41 |
+ expect(template.length).toBe(3); |
|
42 |
+ expect(template[0].length).toBe(8); |
|
43 |
+ expect(template[1].length).toBe(8); |
|
44 |
+ expect(template[2].length).toBe(8); |
|
45 |
+ |
|
46 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 4 }); |
|
47 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 4, endTime: 14 }); |
|
48 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 14, endTime: 17 }); |
|
49 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 17, endTime: 23 }); |
|
50 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 23, endTime: 39 }); |
|
51 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 39, endTime: 54 }); |
|
52 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 54, endTime: 66 }); |
|
53 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 66, endTime: 83 }); |
|
54 |
+ |
|
55 |
+ expect(template[1][0]).toEqual({ processor: 2, startTime: 4, endTime: 8 }); |
|
56 |
+ expect(template[1][1]).toEqual({ processor: 2, startTime: 14, endTime: 16 }); |
|
57 |
+ expect(template[1][2]).toEqual({ processor: 2, startTime: 17, endTime: 29 }); |
|
58 |
+ expect(template[1][3]).toEqual({ processor: 2, startTime: 29, endTime: 42 }); |
|
59 |
+ expect(template[1][4]).toEqual({ processor: 2, startTime: 42, endTime: 49 }); |
|
60 |
+ expect(template[1][5]).toEqual({ processor: 2, startTime: 54, endTime: 64 }); |
|
61 |
+ expect(template[1][6]).toEqual({ processor: 2, startTime: 66, endTime: 74 }); |
|
62 |
+ expect(template[1][7]).toEqual({ processor: 2, startTime: 83, endTime: 89 }); |
|
63 |
+ |
|
64 |
+ expect(template[2][0]).toEqual({ processor: 3, startTime: 8, endTime: 28 }); |
|
65 |
+ expect(template[2][1]).toEqual({ processor: 3, startTime: 28, endTime: 53 }); |
|
66 |
+ expect(template[2][2]).toEqual({ processor: 3, startTime: 53, endTime: 66 }); |
|
67 |
+ expect(template[2][3]).toEqual({ processor: 3, startTime: 66, endTime: 85 }); |
|
68 |
+ expect(template[2][4]).toEqual({ processor: 3, startTime: 85, endTime: 106 }); |
|
69 |
+ expect(template[2][5]).toEqual({ processor: 3, startTime: 106, endTime: 124 }); |
|
70 |
+ expect(template[2][6]).toEqual({ processor: 3, startTime: 124, endTime: 135 }); |
|
71 |
+ expect(template[2][7]).toEqual({ processor: 3, startTime: 135, endTime: 143 }); |
|
72 |
+}); |
|
0 | 73 |
\ No newline at end of file |
1 | 74 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,175 @@ |
0 |
+// import { |
|
1 |
+// addIndex, |
|
2 |
+// findIndex, |
|
3 |
+// find, |
|
4 |
+// map, |
|
5 |
+// propEq, |
|
6 |
+// reduce, |
|
7 |
+// sort, |
|
8 |
+// range, |
|
9 |
+// concat, |
|
10 |
+// filter, |
|
11 |
+// reverse, |
|
12 |
+// prop, |
|
13 |
+// flatten, |
|
14 |
+// uniq, |
|
15 |
+// reject, |
|
16 |
+// contains, |
|
17 |
+// omit, |
|
18 |
+// equals, |
|
19 |
+// update, |
|
20 |
+// times, |
|
21 |
+// add, |
|
22 |
+// pluck, |
|
23 |
+// last, forEach |
|
24 |
+// } from 'rambda'; |
|
25 |
+// import type { JobId } from '../../types'; |
|
26 |
+// import { max, min, remove, slice } from 'ramda'; |
|
27 |
+// import { createSchedule as johnson, updateCTime } from '../johnson'; |
|
28 |
+// |
|
29 |
+// type JobTime = { |
|
30 |
+// jobId: JobId, |
|
31 |
+// t: number, |
|
32 |
+// succ: JobId[], |
|
33 |
+// processor: number |
|
34 |
+// }; |
|
35 |
+// |
|
36 |
+// type JobTimeWithZK = JobTime | { |
|
37 |
+// z: number, |
|
38 |
+// k: number |
|
39 |
+// }; |
|
40 |
+// |
|
41 |
+// const noAncJobs = (jobTimes: JobTime[]) => { |
|
42 |
+// const allSuccesors = flatten(map(prop('succ'), jobTimes)); |
|
43 |
+// return reject((jobTime: JobTime) => contains(jobTime.jobId, allSuccesors), jobTimes); |
|
44 |
+// }; |
|
45 |
+// |
|
46 |
+// const ancestors = (id: JobId, jobTimes: JobTime[]) => |
|
47 |
+// filter(({ succ }) => contains(id, succ), jobTimes); |
|
48 |
+// |
|
49 |
+// const updateZK = (jobTimes: JobTimeWithZK[], allJobTimes: JobTimeWithZK) => |
|
50 |
+// map((jobTime: JobTimeWithZK) => { |
|
51 |
+// // const allSuccesors = map(prop('succ'), jobTimes); |
|
52 |
+// const ancestors = filter(aJob => contains(jobTime.jobId, aJob.succ), allJobTimes); |
|
53 |
+// |
|
54 |
+// let z = reduce((acc, job) => max(acc, job.k), -Infinity, ancestors); |
|
55 |
+// z = max(z, jobTime.t); |
|
56 |
+// |
|
57 |
+// return { |
|
58 |
+// ...jobTime, |
|
59 |
+// z, |
|
60 |
+// k: z + jobTime.t |
|
61 |
+// }; |
|
62 |
+// }, jobTimes); |
|
63 |
+// |
|
64 |
+// export const createSchedule = (jobTimes: JobTime[], processorsCount: number) => { |
|
65 |
+// let k = 0; |
|
66 |
+// let Pk = []; |
|
67 |
+// |
|
68 |
+// let jobTimesWithZK: JobTimeWithZK[] = map((jobTime: JobTime) => ({ |
|
69 |
+// ...jobTime, |
|
70 |
+// z: 0, |
|
71 |
+// k: 0 + jobTime.t |
|
72 |
+// }), jobTimes); |
|
73 |
+// |
|
74 |
+// let Sk = noAncJobs(jobTimesWithZK); |
|
75 |
+// let mStar; |
|
76 |
+// |
|
77 |
+// const processorC = [0, 0, 0, 0]; |
|
78 |
+// |
|
79 |
+// // while (Sk.length) { |
|
80 |
+// while (k < 4) { |
|
81 |
+// // if (k === 2) { |
|
82 |
+// // console.log(processorC); |
|
83 |
+// // } |
|
84 |
+// // console.log(processorC); |
|
85 |
+// let kStar = reduce((acc: JobTimeWithZK, jobTime: JobTimeWithZK) => { |
|
86 |
+// // console.log(jobTime.jobId, jobTime.t + processorC[jobTime.processor]); |
|
87 |
+// |
|
88 |
+// const anc = ancestors(jobTime.jobId, jobTimes)[0]; |
|
89 |
+// // let z = processorC[jobTime.processor]; |
|
90 |
+// |
|
91 |
+// // console.log(jobTime, anc); |
|
92 |
+// |
|
93 |
+// if (anc) { |
|
94 |
+// jobTime.k = jobTime.t + anc.k; |
|
95 |
+// } else { |
|
96 |
+// jobTime.k = jobTime.t; |
|
97 |
+// } |
|
98 |
+// |
|
99 |
+// jobTimes.forEach(j => { |
|
100 |
+// if (j.jobId === jobTime.jobId) { |
|
101 |
+// j.k = jobTime.k |
|
102 |
+// } |
|
103 |
+// }); |
|
104 |
+// |
|
105 |
+// // if (k === 2) { |
|
106 |
+// // console.log(jobTime, anc); |
|
107 |
+// // } |
|
108 |
+// |
|
109 |
+// return jobTime.k < acc.k ? jobTime : acc; |
|
110 |
+// } |
|
111 |
+// // jobTime.t < acc.t ? jobTime : acc |
|
112 |
+// // jobTime.k < acc.k ? jobTime : acc |
|
113 |
+// , { k: Infinity }, Sk).k; |
|
114 |
+// |
|
115 |
+// if (k === 3) |
|
116 |
+// console.log(kStar); |
|
117 |
+// |
|
118 |
+// let oStar = find((jobTime: JobTimeWithZK) => jobTime.k === kStar, Sk); |
|
119 |
+// // console.log(oStar); |
|
120 |
+// mStar = oStar.processor; |
|
121 |
+// |
|
122 |
+// // processorC[oStar.processor] += oStar.t; |
|
123 |
+// // const p = pluck('processor', map(succ => find(propEq('jobId', succ), jobTimes), oStar.succ)); |
|
124 |
+// // forEach(processor => { |
|
125 |
+// // if (processor !== oStar.processor) { |
|
126 |
+// // return processorC[processor] += oStar.t; |
|
127 |
+// // } |
|
128 |
+// // }, p); |
|
129 |
+// |
|
130 |
+// let morePk = filter((jobTime: JobTimeWithZK) => |
|
131 |
+// jobTime.processor === oStar.processor && (jobTime.k - jobTime.t) < kStar |
|
132 |
+// , Sk); |
|
133 |
+// morePk = updateZK(morePk, jobTimesWithZK); |
|
134 |
+// |
|
135 |
+// Pk = concat(Pk, morePk); |
|
136 |
+// let PkIds = pluck('jobId', Pk); |
|
137 |
+// let rejectedFromSk = filter((jobTime: JobTimeWithZK) => contains(jobTime.jobId, PkIds), Sk); |
|
138 |
+// let rejectedSuccessors = flatten( |
|
139 |
+// map(jobTime => map(jobId => find(propEq('jobId', jobId), jobTimesWithZK), jobTime.succ), rejectedFromSk) |
|
140 |
+// ); |
|
141 |
+// // rejectedSuccessors = updateZK(rejectedSuccessors, jobTimesWithZK); |
|
142 |
+// |
|
143 |
+// // Sk = reject(jobTime => jobTime.jobId === oStar.jobId, Sk); |
|
144 |
+// Sk = reject((jobTime: JobTimeWithZK) => contains(jobTime.jobId, PkIds), Sk); |
|
145 |
+// Sk = concat(Sk, rejectedSuccessors); |
|
146 |
+// |
|
147 |
+// Sk = sort((a, b) => a.jobId > b.jobId, Sk); |
|
148 |
+// |
|
149 |
+// // if (k === 1) { |
|
150 |
+// // console.log(Sk); |
|
151 |
+// // } |
|
152 |
+// |
|
153 |
+// k++; |
|
154 |
+// } |
|
155 |
+// |
|
156 |
+// // const jobTimesExtended = map((jobTime: JobTime) => { |
|
157 |
+// // return ({ |
|
158 |
+// // ...jobTime, |
|
159 |
+// // |
|
160 |
+// // }) |
|
161 |
+// // }, jobTimes); |
|
162 |
+// }; |
|
163 |
+// |
|
164 |
+// export default (jobsOperations: JobOperations[], processorsCount: number) => { |
|
165 |
+// const schedule = createSchedule(jobsOperations, processorsCount); |
|
166 |
+// |
|
167 |
+// return times(i => |
|
168 |
+// map((jobTime: JobOperationsWithC) => ({ |
|
169 |
+// processor: i + 1, |
|
170 |
+// startTime: jobTime.operations[i].c - jobTime.operations[i].t, |
|
171 |
+// endTime: jobTime.operations[i].c, |
|
172 |
+// }), schedule) |
|
173 |
+// , processorsCount); |
|
174 |
+// }; |
|
0 | 175 |
\ No newline at end of file |
1 | 176 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,53 @@ |
0 |
+import { map, reduce, sort, range, omit, times } from 'rambda'; |
|
1 |
+import type { JobId } from '../../types'; |
|
2 |
+import { updateCTime } from '../johnson'; |
|
3 |
+import { min } from 'ramda'; |
|
4 |
+ |
|
5 |
+type JobOperations = { |
|
6 |
+ jobId: JobId, |
|
7 |
+ operations: Array<{ t: number }> |
|
8 |
+}; |
|
9 |
+ |
|
10 |
+type JobOperationsWithC = { |
|
11 |
+ jobId: JobId, |
|
12 |
+ operations: Array<{ t: number, c: number }> |
|
13 |
+}; |
|
14 |
+ |
|
15 |
+export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => { |
|
16 |
+ const jobsOperationsWithKoefs = map((jobsOperation: JobOperations) => { |
|
17 |
+ const ej = jobsOperation.operations[0].t < jobsOperation.operations[processorsCount - 1].t |
|
18 |
+ ? 1 : -1; |
|
19 |
+ const daco = reduce( |
|
20 |
+ (acc, i) => min(jobsOperation.operations[i-1].t + jobsOperation.operations[i].t, acc), |
|
21 |
+ Infinity, |
|
22 |
+ range(1, processorsCount) |
|
23 |
+ ); |
|
24 |
+ |
|
25 |
+ const koef = ej / daco; |
|
26 |
+ |
|
27 |
+ return { ...jobsOperation, koef }; |
|
28 |
+ }, jobsOperations); |
|
29 |
+ |
|
30 |
+ let sortedJobsOperations = sort( |
|
31 |
+ (a: JobOperations, b: JobOperations) => { |
|
32 |
+ return b.koef - a.koef; |
|
33 |
+ }, |
|
34 |
+ jobsOperationsWithKoefs |
|
35 |
+ ); |
|
36 |
+ |
|
37 |
+ sortedJobsOperations = map(omit('koef'), sortedJobsOperations); |
|
38 |
+ |
|
39 |
+ return updateCTime(sortedJobsOperations, processorsCount); |
|
40 |
+}; |
|
41 |
+ |
|
42 |
+export default (jobsOperations: JobOperations[], processorsCount: number) => { |
|
43 |
+ const schedule = createSchedule(jobsOperations, processorsCount); |
|
44 |
+ |
|
45 |
+ return times(i => |
|
46 |
+ map((jobTime: JobOperationsWithC) => ({ |
|
47 |
+ processor: i + 1, |
|
48 |
+ startTime: jobTime.operations[i].c - jobTime.operations[i].t, |
|
49 |
+ endTime: jobTime.operations[i].c, |
|
50 |
+ }), schedule) |
|
51 |
+ , processorsCount); |
|
52 |
+}; |
|
0 | 53 |
\ No newline at end of file |
1 | 54 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,73 @@ |
0 |
+import createGantt, { createSchedule } from './index'; |
|
1 |
+ |
|
2 |
+test('create schedule', () => { |
|
3 |
+ const jobsWithOperations = [ |
|
4 |
+ { jobId: 1, operations: [ { t: 12 }, { t: 8 }, { t: 11 } ] }, |
|
5 |
+ { jobId: 2, operations: [ { t: 6 }, { t: 13 }, { t: 19 } ] }, |
|
6 |
+ { jobId: 3, operations: [ { t: 15 }, { t: 10 }, { t: 18 } ] }, |
|
7 |
+ { jobId: 4, operations: [ { t: 16 }, { t: 7 }, { t: 21 } ] }, |
|
8 |
+ { jobId: 5, operations: [ { t: 4 }, { t: 4 }, { t: 20 } ] }, |
|
9 |
+ { jobId: 6, operations: [ { t: 3 }, { t: 12 }, { t: 13 } ] }, |
|
10 |
+ { jobId: 7, operations: [ { t: 17 }, { t: 6 }, { t: 8 } ] }, |
|
11 |
+ { jobId: 8, operations: [ { t: 10 }, { t: 2 }, { t: 25 } ] }, |
|
12 |
+ ]; |
|
13 |
+ |
|
14 |
+ const schedule = createSchedule(jobsWithOperations, 3); |
|
15 |
+ |
|
16 |
+ expect(schedule.length).toBe(8); |
|
17 |
+ expect(schedule[0]).toEqual({ jobId: 5, operations: [ { t: 4, c: 4 }, { t: 4, c: 8 }, { t: 20, c: 28 } ] }); |
|
18 |
+ expect(schedule[1]).toEqual({ jobId: 8, operations: [ { t: 10, c: 14 }, { t: 2, c: 16 }, { t: 25, c: 53 } ] }); |
|
19 |
+ expect(schedule[2]).toEqual({ jobId: 6, operations: [ { t: 3, c: 17 }, { t: 12, c: 29 }, { t: 13, c: 66 } ] }); |
|
20 |
+ expect(schedule[3]).toEqual({ jobId: 2, operations: [ { t: 6, c: 23 }, { t: 13, c: 42 }, { t: 19, c: 85 } ] }); |
|
21 |
+ expect(schedule[4]).toEqual({ jobId: 4, operations: [ { t: 16, c: 39 }, { t: 7, c: 49 }, { t: 21, c: 106 } ] }); |
|
22 |
+ expect(schedule[5]).toEqual({ jobId: 3, operations: [ { t: 15, c: 54 }, { t: 10, c: 64 }, { t: 18, c: 124 } ] }); |
|
23 |
+ expect(schedule[6]).toEqual({ jobId: 1, operations: [ { t: 12, c: 66 }, { t: 8, c: 74 }, { t: 11, c: 135 } ] }); |
|
24 |
+ expect(schedule[7]).toEqual({ jobId: 7, operations: [ { t: 17, c: 83 }, { t: 6, c: 89 }, { t: 8, c: 143 } ] }); |
|
25 |
+}); |
|
26 |
+ |
|
27 |
+test('create gantt', () => { |
|
28 |
+ const jobsWithOperations = [ |
|
29 |
+ { jobId: 1, operations: [ { t: 12 }, { t: 8 }, { t: 11 } ] }, |
|
30 |
+ { jobId: 2, operations: [ { t: 6 }, { t: 13 }, { t: 19 } ] }, |
|
31 |
+ { jobId: 3, operations: [ { t: 15 }, { t: 10 }, { t: 18 } ] }, |
|
32 |
+ { jobId: 4, operations: [ { t: 16 }, { t: 7 }, { t: 21 } ] }, |
|
33 |
+ { jobId: 5, operations: [ { t: 4 }, { t: 4 }, { t: 20 } ] }, |
|
34 |
+ { jobId: 6, operations: [ { t: 3 }, { t: 12 }, { t: 13 } ] }, |
|
35 |
+ { jobId: 7, operations: [ { t: 17 }, { t: 6 }, { t: 8 } ] }, |
|
36 |
+ { jobId: 8, operations: [ { t: 10 }, { t: 2 }, { t: 25 } ] }, |
|
37 |
+ ]; |
|
38 |
+ |
|
39 |
+ const template = createGantt(jobsWithOperations, 3); |
|
40 |
+ |
|
41 |
+ expect(template.length).toBe(3); |
|
42 |
+ expect(template[0].length).toBe(8); |
|
43 |
+ expect(template[1].length).toBe(8); |
|
44 |
+ expect(template[2].length).toBe(8); |
|
45 |
+ |
|
46 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 4 }); |
|
47 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 4, endTime: 14 }); |
|
48 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 14, endTime: 17 }); |
|
49 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 17, endTime: 23 }); |
|
50 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 23, endTime: 39 }); |
|
51 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 39, endTime: 54 }); |
|
52 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 54, endTime: 66 }); |
|
53 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 66, endTime: 83 }); |
|
54 |
+ |
|
55 |
+ expect(template[1][0]).toEqual({ processor: 2, startTime: 4, endTime: 8 }); |
|
56 |
+ expect(template[1][1]).toEqual({ processor: 2, startTime: 14, endTime: 16 }); |
|
57 |
+ expect(template[1][2]).toEqual({ processor: 2, startTime: 17, endTime: 29 }); |
|
58 |
+ expect(template[1][3]).toEqual({ processor: 2, startTime: 29, endTime: 42 }); |
|
59 |
+ expect(template[1][4]).toEqual({ processor: 2, startTime: 42, endTime: 49 }); |
|
60 |
+ expect(template[1][5]).toEqual({ processor: 2, startTime: 54, endTime: 64 }); |
|
61 |
+ expect(template[1][6]).toEqual({ processor: 2, startTime: 66, endTime: 74 }); |
|
62 |
+ expect(template[1][7]).toEqual({ processor: 2, startTime: 83, endTime: 89 }); |
|
63 |
+ |
|
64 |
+ expect(template[2][0]).toEqual({ processor: 3, startTime: 8, endTime: 28 }); |
|
65 |
+ expect(template[2][1]).toEqual({ processor: 3, startTime: 28, endTime: 53 }); |
|
66 |
+ expect(template[2][2]).toEqual({ processor: 3, startTime: 53, endTime: 66 }); |
|
67 |
+ expect(template[2][3]).toEqual({ processor: 3, startTime: 66, endTime: 85 }); |
|
68 |
+ expect(template[2][4]).toEqual({ processor: 3, startTime: 85, endTime: 106 }); |
|
69 |
+ expect(template[2][5]).toEqual({ processor: 3, startTime: 106, endTime: 124 }); |
|
70 |
+ expect(template[2][6]).toEqual({ processor: 3, startTime: 124, endTime: 135 }); |
|
71 |
+ expect(template[2][7]).toEqual({ processor: 3, startTime: 135, endTime: 143 }); |
|
72 |
+}); |
|
0 | 73 |
\ No newline at end of file |
1 | 74 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,104 @@ |
0 |
+import { |
|
1 |
+ addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
2 |
+ filter, reverse, prop, flatten, uniq, reject, contains, omit, equals, update, times |
|
3 |
+} from 'rambda'; |
|
4 |
+import type { JobId } from '../../types'; |
|
5 |
+import { max, min, remove, slice } from 'ramda'; |
|
6 |
+ |
|
7 |
+type JobOperations = { |
|
8 |
+ jobId: JobId, |
|
9 |
+ operations: Array<{ t: number }> |
|
10 |
+}; |
|
11 |
+ |
|
12 |
+type JobOperationsWithC = { |
|
13 |
+ jobId: JobId, |
|
14 |
+ operations: Array<{ t: number, c: number }> |
|
15 |
+}; |
|
16 |
+ |
|
17 |
+const findMinT = (operations) => |
|
18 |
+ reduce( |
|
19 |
+ (t1, t2) => t1.t < t2.t ? t1 : t2, |
|
20 |
+ { t: Infinity }, |
|
21 |
+ operations |
|
22 |
+ ); |
|
23 |
+ |
|
24 |
+const findMinJob = (jobsOperations: JobOperations[]): JobOperations => |
|
25 |
+ reduce( |
|
26 |
+ (minJob: JobOperations, jobOperations: JobOperations) => { |
|
27 |
+ const minJobT = findMinT(minJob.operations); |
|
28 |
+ const jobOperationsT = findMinT(jobOperations.operations); |
|
29 |
+ |
|
30 |
+ return minJobT.t < jobOperationsT.t ? minJob : jobOperations; |
|
31 |
+ }, |
|
32 |
+ { operations: [ { t: Infinity }, { t: Infinity } ] }, |
|
33 |
+ jobsOperations |
|
34 |
+ ); |
|
35 |
+ |
|
36 |
+export const updateCTime = (jobsOperations: JobOperations[], processorCount: number): JobOperationsWithC[] => { |
|
37 |
+ let prev = 0; |
|
38 |
+ const cj = []; |
|
39 |
+ |
|
40 |
+ cj[0] = map((jobTime: JobOperations) => { |
|
41 |
+ prev += jobTime.operations[0].t; |
|
42 |
+ const operationC = { ...jobTime.operations[0], c: prev }; |
|
43 |
+ const operations = update(0, operationC, jobTime.operations); |
|
44 |
+ |
|
45 |
+ return { ...jobTime, operations }; |
|
46 |
+ }, jobsOperations); |
|
47 |
+ |
|
48 |
+ if (processorCount > 1) { |
|
49 |
+ times(i => { |
|
50 |
+ const processorI = i + 1; |
|
51 |
+ prev = cj[0][0].operations[0].c; |
|
52 |
+ |
|
53 |
+ cj[processorI] = map((jobTime: JobOperations) => { |
|
54 |
+ const conflicts = filter(({ c }) => prev < c, jobTime.operations); |
|
55 |
+ if (conflicts.length) { |
|
56 |
+ const maxConflict = sort((a, b) => b.c - a.c, conflicts)[0]; |
|
57 |
+ prev = maxConflict.c; |
|
58 |
+ } |
|
59 |
+ |
|
60 |
+ prev += jobTime.operations[processorI].t; |
|
61 |
+ |
|
62 |
+ const operationC = { ...jobTime.operations[processorI], c: prev }; |
|
63 |
+ const operations = update(processorI, operationC, jobTime.operations); |
|
64 |
+ |
|
65 |
+ return { ...jobTime, operations }; |
|
66 |
+ }, cj[i]); |
|
67 |
+ }, processorCount - 1); |
|
68 |
+ } |
|
69 |
+ |
|
70 |
+ return cj[processorCount - 1]; |
|
71 |
+}; |
|
72 |
+ |
|
73 |
+export const createSchedule = (jobsOperations: JobOperations[]): JobOperationsWithC[] => { |
|
74 |
+ let I = jobsOperations; |
|
75 |
+ let L = [[], []]; |
|
76 |
+ |
|
77 |
+ while (I.length) { |
|
78 |
+ const minTJob = findMinJob(I); |
|
79 |
+ |
|
80 |
+ const { t: minT } = findMinT(minTJob.operations); |
|
81 |
+ const operationIndex = findIndex(o => o.t === minT, minTJob.operations); |
|
82 |
+ const minTJobIndex = findIndex(equals(minTJob), I); |
|
83 |
+ |
|
84 |
+ L[operationIndex].push(minTJob); |
|
85 |
+ I = remove(minTJobIndex, 1, I); |
|
86 |
+ } |
|
87 |
+ |
|
88 |
+ const LFinal = concat(L[0], reverse(L[1])); |
|
89 |
+ |
|
90 |
+ return updateCTime(LFinal, 2); |
|
91 |
+}; |
|
92 |
+ |
|
93 |
+export default (jobsOperations: JobOperations[]) => { |
|
94 |
+ const schedule = createSchedule(jobsOperations); |
|
95 |
+ |
|
96 |
+ return times(i => |
|
97 |
+ map((jobTime: JobOperationsWithC) => ({ |
|
98 |
+ processor: i + 1, |
|
99 |
+ startTime: jobTime.operations[i].c - jobTime.operations[i].t, |
|
100 |
+ endTime: jobTime.operations[i].c, |
|
101 |
+ }), schedule) |
|
102 |
+ , 2); |
|
103 |
+}; |
|
0 | 104 |
\ No newline at end of file |
1 | 105 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,73 @@ |
0 |
+import createGantt, { createSchedule } from './index'; |
|
1 |
+ |
|
2 |
+test('create schedule', () => { |
|
3 |
+ const jobsWithOperations = [ |
|
4 |
+ { jobId: 1, operations: [ { t: 15 }, { t: 11 } ] }, |
|
5 |
+ { jobId: 2, operations: [ { t: 20 }, { t: 15 } ] }, |
|
6 |
+ { jobId: 3, operations: [ { t: 35 }, { t: 5 } ] }, |
|
7 |
+ { jobId: 4, operations: [ { t: 10 }, { t: 31 } ] }, |
|
8 |
+ { jobId: 5, operations: [ { t: 18 }, { t: 16 } ] }, |
|
9 |
+ { jobId: 6, operations: [ { t: 17 }, { t: 26 } ] }, |
|
10 |
+ { jobId: 7, operations: [ { t: 9 }, { t: 32 } ] }, |
|
11 |
+ { jobId: 8, operations: [ { t: 44 }, { t: 6 } ] }, |
|
12 |
+ { jobId: 9, operations: [ { t: 29 }, { t: 18 } ] }, |
|
13 |
+ { jobId: 10, operations: [ { t: 21 }, { t: 22 } ] }, |
|
14 |
+ ]; |
|
15 |
+ |
|
16 |
+ const schedule = createSchedule(jobsWithOperations); |
|
17 |
+ |
|
18 |
+ expect(schedule.length).toBe(10); |
|
19 |
+ expect(schedule[0]).toEqual({ jobId: 7, operations: [ { t: 9, c: 9 }, { t: 32, c: 41 } ] }); |
|
20 |
+ expect(schedule[1]).toEqual({ jobId: 4, operations: [ { t: 10, c: 19 }, { t: 31, c: 72 } ] }); |
|
21 |
+ expect(schedule[2]).toEqual({ jobId: 6, operations: [ { t: 17, c: 36 }, { t: 26, c: 98 } ] }); |
|
22 |
+ expect(schedule[3]).toEqual({ jobId: 10, operations: [ { t: 21, c: 57 }, { t: 22, c: 120 } ] }); |
|
23 |
+ expect(schedule[4]).toEqual({ jobId: 9, operations: [ { t: 29, c: 86 }, { t: 18, c: 138 } ] }); |
|
24 |
+ expect(schedule[5]).toEqual({ jobId: 5, operations: [ { t: 18, c: 104 }, { t: 16, c: 154 } ] }); |
|
25 |
+ expect(schedule[6]).toEqual({ jobId: 2, operations: [ { t: 20, c: 124 }, { t: 15, c: 169 } ] }); |
|
26 |
+ expect(schedule[7]).toEqual({ jobId: 1, operations: [ { t: 15, c: 139 }, { t: 11, c: 180 } ] }); |
|
27 |
+ expect(schedule[8]).toEqual({ jobId: 8, operations: [ { t: 44, c: 183 }, { t: 6, c: 189 } ] }); |
|
28 |
+ expect(schedule[9]).toEqual({ jobId: 3, operations: [ { t: 35, c: 218 }, { t: 5, c: 223 } ] }); |
|
29 |
+}); |
|
30 |
+ |
|
31 |
+test('create gantt', () => { |
|
32 |
+ const jobsWithOperations = [ |
|
33 |
+ { jobId: 1, operations: [ { t: 15 }, { t: 11 } ] }, |
|
34 |
+ { jobId: 2, operations: [ { t: 20 }, { t: 15 } ] }, |
|
35 |
+ { jobId: 3, operations: [ { t: 35 }, { t: 5 } ] }, |
|
36 |
+ { jobId: 4, operations: [ { t: 10 }, { t: 31 } ] }, |
|
37 |
+ { jobId: 5, operations: [ { t: 18 }, { t: 16 } ] }, |
|
38 |
+ { jobId: 6, operations: [ { t: 17 }, { t: 26 } ] }, |
|
39 |
+ { jobId: 7, operations: [ { t: 9 }, { t: 32 } ] }, |
|
40 |
+ { jobId: 8, operations: [ { t: 44 }, { t: 6 } ] }, |
|
41 |
+ { jobId: 9, operations: [ { t: 29 }, { t: 18 } ] }, |
|
42 |
+ { jobId: 10, operations: [ { t: 21 }, { t: 22 } ] }, |
|
43 |
+ ]; |
|
44 |
+ |
|
45 |
+ const template = createGantt(jobsWithOperations); |
|
46 |
+ |
|
47 |
+ expect(template.length).toBe(2); |
|
48 |
+ expect(template[0].length).toBe(10); |
|
49 |
+ expect(template[1].length).toBe(10); |
|
50 |
+ |
|
51 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 9 }); |
|
52 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 9, endTime: 19 }); |
|
53 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 19, endTime: 36 }); |
|
54 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 36, endTime: 57 }); |
|
55 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 57, endTime: 86 }); |
|
56 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 86, endTime: 104 }); |
|
57 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 104, endTime: 124 }); |
|
58 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 124, endTime: 139 }); |
|
59 |
+ expect(template[0][8]).toEqual({ processor: 1, startTime: 139, endTime: 183 }); |
|
60 |
+ expect(template[0][9]).toEqual({ processor: 1, startTime: 183, endTime: 218 }); |
|
61 |
+ |
|
62 |
+ expect(template[1][0]).toEqual({ processor: 2, startTime: 9, endTime: 41 }); |
|
63 |
+ expect(template[1][1]).toEqual({ processor: 2, startTime: 41, endTime: 72 }); |
|
64 |
+ expect(template[1][2]).toEqual({ processor: 2, startTime: 72, endTime: 98 }); |
|
65 |
+ expect(template[1][3]).toEqual({ processor: 2, startTime: 98, endTime: 120 }); |
|
66 |
+ expect(template[1][4]).toEqual({ processor: 2, startTime: 120, endTime: 138 }); |
|
67 |
+ expect(template[1][5]).toEqual({ processor: 2, startTime: 138, endTime: 154 }); |
|
68 |
+ expect(template[1][6]).toEqual({ processor: 2, startTime: 154, endTime: 169 }); |
|
69 |
+ expect(template[1][7]).toEqual({ processor: 2, startTime: 169, endTime: 180 }); |
|
70 |
+ expect(template[1][8]).toEqual({ processor: 2, startTime: 183, endTime: 189 }); |
|
71 |
+ expect(template[1][9]).toEqual({ processor: 2, startTime: 218, endTime: 223 }); |
|
72 |
+}); |
|
0 | 73 |
\ No newline at end of file |
1 | 74 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,86 @@ |
0 |
+import { |
|
1 |
+ addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
2 |
+ filter, reverse, prop, flatten, uniq, reject, contains, omit |
|
3 |
+} from 'rambda'; |
|
4 |
+import type { JobId } from '../../types'; |
|
5 |
+import { max, remove, slice } from 'ramda'; |
|
6 |
+ |
|
7 |
+type JobTime = { |
|
8 |
+ jobId: JobId, |
|
9 |
+ t: number, |
|
10 |
+ d: number, |
|
11 |
+ c: number, |
|
12 |
+ w: number, |
|
13 |
+ anc: JobId[], |
|
14 |
+}; |
|
15 |
+ |
|
16 |
+const findMaxTJob = (jobTimes: JobTime[]) => |
|
17 |
+ reduce( |
|
18 |
+ (maxTJob: JobTime, jobTime: JobTime) => maxTJob.t >= jobTime.t ? maxTJob : jobTime, |
|
19 |
+ { t: -Infinity }, |
|
20 |
+ jobTimes |
|
21 |
+ ); |
|
22 |
+ |
|
23 |
+const findMinJob = (fi: number, jobTimes: JobTime[]) => |
|
24 |
+ reduce( |
|
25 |
+ (minJob: JobTime, jobTime: JobTime) => { |
|
26 |
+ const minJobU = minJob.w * max(fi - minJob.d, 0); |
|
27 |
+ const jobTimeU = jobTime.w * max(fi - jobTime.d, 0); |
|
28 |
+ |
|
29 |
+ return minJobU < jobTimeU ? minJob : jobTime; |
|
30 |
+ }, |
|
31 |
+ { w: Infinity }, |
|
32 |
+ jobTimes |
|
33 |
+ ); |
|
34 |
+ |
|
35 |
+const updateCTime = (jobTimes: JobTime[]) => { |
|
36 |
+ let prev = 0; |
|
37 |
+ |
|
38 |
+ const cj = map((jobTime: JobTime) => { |
|
39 |
+ prev += jobTime.t; |
|
40 |
+ |
|
41 |
+ return { ...jobTime, c: prev }; |
|
42 |
+ }, jobTimes); |
|
43 |
+ |
|
44 |
+ return cj; |
|
45 |
+}; |
|
46 |
+ |
|
47 |
+export const createSchedule = (jobs: JobTime[]) => { |
|
48 |
+ let fi = reduce((sum, job: JobTime) => sum + job.t, 0, jobs); |
|
49 |
+ let jobsD = jobs; |
|
50 |
+ let R = []; |
|
51 |
+ |
|
52 |
+ while (fi > 0) { |
|
53 |
+ const allAcc = uniq(flatten( |
|
54 |
+ map(prop('anc'), jobsD) |
|
55 |
+ )); |
|
56 |
+ |
|
57 |
+ const G = reject((jobTime: JobTime) => contains(jobTime.jobId, allAcc), jobsD); |
|
58 |
+ |
|
59 |
+ if (!G.length) return null; |
|
60 |
+ |
|
61 |
+ const minJob = findMinJob(fi, G); |
|
62 |
+ const minJobIndex = findIndex(propEq('jobId', minJob.jobId), jobsD); |
|
63 |
+ |
|
64 |
+ jobsD = remove(minJobIndex, 1, jobsD); |
|
65 |
+ fi -= minJob.t; |
|
66 |
+ |
|
67 |
+ R.push(minJob); |
|
68 |
+ } |
|
69 |
+ |
|
70 |
+ const withC = updateCTime(reverse(R)); |
|
71 |
+ |
|
72 |
+ return map(omit(['w', 'anc']), withC); |
|
73 |
+}; |
|
74 |
+ |
|
75 |
+export default (jobs: JobTime[]) => { |
|
76 |
+ const schedule = createSchedule(jobs); |
|
77 |
+ |
|
78 |
+ const normalizedSchedule = map((jobTime: JobTime) => ({ |
|
79 |
+ processor: 1, |
|
80 |
+ startTime: jobTime.c - jobTime.t, |
|
81 |
+ endTime: jobTime.c, |
|
82 |
+ }), schedule); |
|
83 |
+ |
|
84 |
+ return [normalizedSchedule]; |
|
85 |
+}; |
|
0 | 86 |
\ No newline at end of file |
1 | 87 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,64 @@ |
0 |
+import createGantt, { createSchedule } from './index'; |
|
1 |
+ |
|
2 |
+test('create schedule', () => { |
|
3 |
+ const jobs = [ |
|
4 |
+ { jobId: 1, t: 8, d: 11, anc: [], w: 1 }, |
|
5 |
+ { jobId: 2, t: 5, d: 9, anc: [], w: 1 }, |
|
6 |
+ { jobId: 3, t: 6, d: 10, anc: [], w: 3 }, |
|
7 |
+ { jobId: 4, t: 5, d: 14, anc: [1], w: 2 }, |
|
8 |
+ { jobId: 5, t: 9, d: 19, anc: [1], w: 1 }, |
|
9 |
+ { jobId: 6, t: 3, d: 14, anc: [2, 3, 4], w: 1 }, |
|
10 |
+ { jobId: 7, t: 9, d: 36, anc: [2, 3, 4], w: 2 }, |
|
11 |
+ { jobId: 8, t: 5, d: 39, anc: [2, 3, 4], w: 2 }, |
|
12 |
+ { jobId: 9, t: 8, d: 31, anc: [5, 6], w: 1 }, |
|
13 |
+ { jobId: 10, t: 5, d: 59, anc: [7], w: 2 }, |
|
14 |
+ { jobId: 11, t: 5, d: 61, anc: [8], w: 3 }, |
|
15 |
+ ]; |
|
16 |
+ |
|
17 |
+ const schedule = createSchedule(jobs); |
|
18 |
+ |
|
19 |
+ expect(schedule.length).toBe(11); |
|
20 |
+ expect(schedule[0]).toEqual({ jobId: 3, t: 6, d: 10, c: 6 }); |
|
21 |
+ expect(schedule[1]).toEqual({ jobId: 1, t: 8, d: 11, c: 14 }); |
|
22 |
+ expect(schedule[2]).toEqual({ jobId: 4, t: 5, d: 14, c: 19 }); |
|
23 |
+ expect(schedule[3]).toEqual({ jobId: 2, t: 5, d: 9, c: 24 }); |
|
24 |
+ expect(schedule[4]).toEqual({ jobId: 6, t: 3, d: 14, c: 27 }); |
|
25 |
+ expect(schedule[5]).toEqual({ jobId: 5, t: 9, d: 19, c: 36 }); |
|
26 |
+ expect(schedule[6]).toEqual({ jobId: 7, t: 9, d: 36, c: 45 }); |
|
27 |
+ expect(schedule[7]).toEqual({ jobId: 8, t: 5, d: 39, c: 50 }); |
|
28 |
+ expect(schedule[8]).toEqual({ jobId: 9, t: 8, d: 31, c: 58 }); |
|
29 |
+ expect(schedule[9]).toEqual({ jobId: 11, t: 5, d: 61, c: 63 }); |
|
30 |
+ expect(schedule[10]).toEqual({ jobId: 10, t: 5, d: 59, c: 68 }); |
|
31 |
+}); |
|
32 |
+ |
|
33 |
+test('create gantt', () => { |
|
34 |
+ const jobs = [ |
|
35 |
+ { jobId: 1, t: 8, d: 11, anc: [], w: 1 }, |
|
36 |
+ { jobId: 2, t: 5, d: 9, anc: [], w: 1 }, |
|
37 |
+ { jobId: 3, t: 6, d: 10, anc: [], w: 3 }, |
|
38 |
+ { jobId: 4, t: 5, d: 14, anc: [1], w: 2 }, |
|
39 |
+ { jobId: 5, t: 9, d: 19, anc: [1], w: 1 }, |
|
40 |
+ { jobId: 6, t: 3, d: 14, anc: [2, 3, 4], w: 1 }, |
|
41 |
+ { jobId: 7, t: 9, d: 36, anc: [2, 3, 4], w: 2 }, |
|
42 |
+ { jobId: 8, t: 5, d: 39, anc: [2, 3, 4], w: 2 }, |
|
43 |
+ { jobId: 9, t: 8, d: 31, anc: [5, 6], w: 1 }, |
|
44 |
+ { jobId: 10, t: 5, d: 59, anc: [7], w: 2 }, |
|
45 |
+ { jobId: 11, t: 5, d: 61, anc: [8], w: 3 }, |
|
46 |
+ ]; |
|
47 |
+ |
|
48 |
+ const template = createGantt(jobs); |
|
49 |
+ |
|
50 |
+ expect(template.length).toBe(1); |
|
51 |
+ expect(template[0].length).toBe(11); |
|
52 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6 }); |
|
53 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 14 }); |
|
54 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 14, endTime: 19 }); |
|
55 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 19, endTime: 24 }); |
|
56 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 24, endTime: 27 }); |
|
57 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 27, endTime: 36 }); |
|
58 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 36, endTime: 45 }); |
|
59 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 45, endTime: 50 }); |
|
60 |
+ expect(template[0][8]).toEqual({ processor: 1, startTime: 50, endTime: 58 }); |
|
61 |
+ expect(template[0][9]).toEqual({ processor: 1, startTime: 58, endTime: 63 }); |
|
62 |
+ expect(template[0][10]).toEqual({ processor: 1, startTime: 63, endTime: 68 }); |
|
63 |
+}); |
|
0 | 64 |
\ No newline at end of file |
1 | 65 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,86 @@ |
0 |
+import { |
|
1 |
+ addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
2 |
+ filter, reverse, prop, flatten, uniq, reject, contains, omit, times |
|
3 |
+} from 'rambda'; |
|
4 |
+import type { JobId } from '../../types'; |
|
5 |
+import { groupBy, max, remove, slice } from 'ramda'; |
|
6 |
+ |
|
7 |
+type JobTime = { |
|
8 |
+ jobId: JobId, |
|
9 |
+ t: number |
|
10 |
+}; |
|
11 |
+ |
|
12 |
+type JobTimeAssigned = JobTime | { |
|
13 |
+ processor: number, |
|
14 |
+}; |
|
15 |
+ |
|
16 |
+type JobTimeAssignedWithC = JobTimeAssigned | { |
|
17 |
+ c: number, |
|
18 |
+}; |
|
19 |
+ |
|
20 |
+export const updateCTime = (jobTimes: JobTimeAssigned[]) => { |
|
21 |
+ let prev = 0; |
|
22 |
+ let prevProcessor = 0; |
|
23 |
+ |
|
24 |
+ const cj = map((jobTime: JobTimeAssigned) => { |
|
25 |
+ if (jobTime.processor !== prevProcessor) { |
|
26 |
+ prev = 0; |
|
27 |
+ } |
|
28 |
+ |
|
29 |
+ prev += jobTime.t; |
|
30 |
+ prevProcessor = jobTime.processor; |
|
31 |
+ |
|
32 |
+ return { ...jobTime, c: prev }; |
|
33 |
+ }, jobTimes); |
|
34 |
+ |
|
35 |
+ return cj; |
|
36 |
+}; |
|
37 |
+ |
|
38 |
+export const createSchedule = (jobs: JobTime[], processorCount: number): JobTimeAssignedWithC[] => { |
|
39 |
+ let G = jobs; |
|
40 |
+ const sumT = reduce((acc, job) => acc + job.t, 0, G); |
|
41 |
+ const maxT = reduce((acc, job) => max(acc, job.t), -Infinity, G); |
|
42 |
+ const K = max(Math.ceil(sumT / processorCount), maxT); |
|
43 |
+ const R = []; |
|
44 |
+ |
|
45 |
+ let processorC = times(() => 0, processorCount + 1); |
|
46 |
+ let activeProcessor = 1; |
|
47 |
+ |
|
48 |
+ let i = 0; |
|
49 |
+ |
|
50 |
+ while (G.length) { |
|
51 |
+ if (processorC[activeProcessor] + G[0].t <= K) { |
|
52 |
+ processorC[activeProcessor] += G[0].t; |
|
53 |
+ R.push({ ...G[0], processor: activeProcessor }); |
|
54 |
+ [, ...G] = G; |
|
55 |
+ |
|
56 |
+ if (processorC[activeProcessor] === K) { |
|
57 |
+ activeProcessor++; |
|
58 |
+ } |
|
59 |
+ } else { |
|
60 |
+ const tDiff = processorC[activeProcessor] + G[0].t - K; |
|
61 |
+ |
|
62 |
+ R.push({ ...G[0], t: G[0].t - tDiff, processor: activeProcessor }); |
|
63 |
+ G[0].t = tDiff; |
|
64 |
+ |
|
65 |
+ activeProcessor++; |
|
66 |
+ } |
|
67 |
+ |
|
68 |
+ i++; |
|
69 |
+ } |
|
70 |
+ |
|
71 |
+ const withC = updateCTime(R); |
|
72 |
+ return withC; |
|
73 |
+}; |
|
74 |
+ |
|
75 |
+export default (jobs: JobTime[], processorCount: number) => { |
|
76 |
+ const schedule = createSchedule(jobs, processorCount); |
|
77 |
+ |
|
78 |
+ const grouped = Object.values(groupBy(jobTime => jobTime.processor, schedule)); |
|
79 |
+ |
|
80 |
+ return map(map((job: JobTimeAssignedWithC) => ({ |
|
81 |
+ processor: job.processor, |
|
82 |
+ startTime: job.c - job.t, |
|
83 |
+ endTime: job.c |
|
84 |
+ })), grouped); |
|
85 |
+}; |
|
0 | 86 |
\ No newline at end of file |
1 | 87 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,46 @@ |
0 |
+import createGantt, { createSchedule } from './index'; |
|
1 |
+ |
|
2 |
+test('create schedule', () => { |
|
3 |
+ const jobs = [ |
|
4 |
+ { jobId: 1, t: 3 }, |
|
5 |
+ { jobId: 2, t: 9 }, |
|
6 |
+ { jobId: 3, t: 5 }, |
|
7 |
+ { jobId: 4, t: 7 }, |
|
8 |
+ { jobId: 5, t: 6 }, |
|
9 |
+ ]; |
|
10 |
+ |
|
11 |
+ const schedule = createSchedule(jobs, 3); |
|
12 |
+ |
|
13 |
+ expect(schedule.length).toBe(7); |
|
14 |
+ expect(schedule[0]).toEqual({ jobId: 1, t: 3, c: 3, processor: 1 }); |
|
15 |
+ expect(schedule[1]).toEqual({ jobId: 2, t: 7, c: 10, processor: 1 }); |
|
16 |
+ expect(schedule[2]).toEqual({ jobId: 2, t: 2, c: 2, processor: 2 }); |
|
17 |
+ expect(schedule[3]).toEqual({ jobId: 3, t: 5, c: 7, processor: 2 }); |
|
18 |
+ expect(schedule[4]).toEqual({ jobId: 4, t: 3, c: 10, processor: 2 }); |
|
19 |
+ expect(schedule[5]).toEqual({ jobId: 4, t: 4, c: 4, processor: 3 }); |
|
20 |
+ expect(schedule[6]).toEqual({ jobId: 5, t: 6, c: 10, processor: 3 }); |
|
21 |
+}); |
|
22 |
+ |
|
23 |
+test('create gantt', () => { |
|
24 |
+ const jobs = [ |
|
25 |
+ { jobId: 1, t: 3 }, |
|
26 |
+ { jobId: 2, t: 9 }, |
|
27 |
+ { jobId: 3, t: 5 }, |
|
28 |
+ { jobId: 4, t: 7 }, |
|
29 |
+ { jobId: 5, t: 6 }, |
|
30 |
+ ]; |
|
31 |
+ |
|
32 |
+ const template = createGantt(jobs, 3); |
|
33 |
+ |
|
34 |
+ expect(template.length).toBe(3); |
|
35 |
+ expect(template[0].length).toBe(2); |
|
36 |
+ expect(template[1].length).toBe(3); |
|
37 |
+ expect(template[2].length).toBe(2); |
|
38 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 3 }); |
|
39 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 3, endTime: 10 }); |
|
40 |
+ expect(template[1][0]).toEqual({ processor: 2, startTime: 0, endTime: 2 }); |
|
41 |
+ expect(template[1][1]).toEqual({ processor: 2, startTime: 2, endTime: 7 }); |
|
42 |
+ expect(template[1][2]).toEqual({ processor: 2, startTime: 7, endTime: 10 }); |
|
43 |
+ expect(template[2][0]).toEqual({ processor: 3, startTime: 0, endTime: 4 }); |
|
44 |
+ expect(template[2][1]).toEqual({ processor: 3, startTime: 4, endTime: 10 }); |
|
45 |
+}); |
|
0 | 46 |
\ No newline at end of file |
1 | 47 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,76 @@ |
0 |
+import { addIndex, findIndex, find, map, propEq, reduce, sort, range, concat } from 'rambda'; |
|
1 |
+import type { JobId } from '../../types'; |
|
2 |
+import { remove, slice } from 'ramda'; |
|
3 |
+ |
|
4 |
+type JobTime = { |
|
5 |
+ jobId: JobId, |
|
6 |
+ t: number, |
|
7 |
+ d: number, |
|
8 |
+ c: number |
|
9 |
+}; |
|
10 |
+ |
|
11 |
+const mapWithIndex = addIndex(map); |
|
12 |
+ |
|
13 |
+// const byAsc = (a: JobTime, b: JobTime) => a.time - b.time; |
|
14 |
+ |
|
15 |
+const findJobById = (jobId: JobId, jobTimes: JobTime[]): JobTime => |
|
16 |
+ find(propEq('jobId', jobId), jobTimes); |
|
17 |
+ |
|
18 |
+const findLateJob = (jobTimes: JobTime[]) => |
|
19 |
+ find(({ c, d }: JobTime) => c > d, jobTimes); |
|
20 |
+ |
|
21 |
+const findMaxTJob = (jobTimes: JobTime[]) => |
|
22 |
+ reduce( |
|
23 |
+ (maxTJob: JobTime, jobTime: JobTime) => maxTJob.t > jobTime.t ? maxTJob : jobTime, |
|
24 |
+ { t: -Infinity }, |
|
25 |
+ jobTimes |
|
26 |
+ ); |
|
27 |
+ |
|
28 |
+const updateCTime = (jobTimes: JobTime[]) => { |
|
29 |
+ let prev = 0; |
|
30 |
+ |
|
31 |
+ const cj = map((jobTime: JobTime) => { |
|
32 |
+ prev += jobTime.t; |
|
33 |
+ |
|
34 |
+ return { ...jobTime, c: prev }; |
|
35 |
+ }, jobTimes); |
|
36 |
+ |
|
37 |
+ return cj; |
|
38 |
+}; |
|
39 |
+ |
|
40 |
+export const createSchedule = (jobs: JobTime[]) => { |
|
41 |
+ const F = sort((a: JobTime, b: JobTime) => a.d - b.d, jobs); |
|
42 |
+ const L = []; |
|
43 |
+ |
|
44 |
+ let cj = updateCTime(F); |
|
45 |
+ let lateJob; |
|
46 |
+ |
|
47 |
+ while (lateJob = findLateJob(cj)) { |
|
48 |
+ const lateJobIndex = findIndex(propEq('jobId', lateJob.jobId), cj); |
|
49 |
+ const jobsBeforeLateJob = slice(0, lateJobIndex + 1, cj); |
|
50 |
+ |
|
51 |
+ const maxTJob = findMaxTJob(jobsBeforeLateJob); |
|
52 |
+ const maxTJobIndex = findIndex(propEq('jobId', maxTJob.jobId), cj); |
|
53 |
+ |
|
54 |
+ cj = remove(maxTJobIndex, 1, cj); |
|
55 |
+ cj = updateCTime(cj); |
|
56 |
+ |
|
57 |
+ L.push(maxTJob); |
|
58 |
+ } |
|
59 |
+ |
|
60 |
+ const schedule = concat(cj, L); |
|
61 |
+ |
|
62 |
+ return updateCTime(schedule); |
|
63 |
+}; |
|
64 |
+ |
|
65 |
+export default (jobs: JobTime[]) => { |
|
66 |
+ const schedule = createSchedule(jobs); |
|
67 |
+ |
|
68 |
+ const normalizedSchedule = map((jobTime: JobTime) => ({ |
|
69 |
+ processor: 1, |
|
70 |
+ startTime: jobTime.c - jobTime.t, |
|
71 |
+ endTime: jobTime.c, |
|
72 |
+ }), schedule); |
|
73 |
+ |
|
74 |
+ return [normalizedSchedule]; |
|
75 |
+}; |
|
0 | 76 |
\ No newline at end of file |
1 | 77 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,56 @@ |
0 |
+import createGantt, { createSchedule } from './index'; |
|
1 |
+ |
|
2 |
+test('create schedule', () => { |
|
3 |
+ const jobs = [ |
|
4 |
+ { jobId: 1, t: 6, d: 10 }, |
|
5 |
+ { jobId: 2, t: 8, d: 25 }, |
|
6 |
+ { jobId: 3, t: 4, d: 20 }, |
|
7 |
+ { jobId: 4, t: 6, d: 18 }, |
|
8 |
+ { jobId: 5, t: 7, d: 20 }, |
|
9 |
+ { jobId: 6, t: 4, d: 40 }, |
|
10 |
+ { jobId: 7, t: 3, d: 35 }, |
|
11 |
+ { jobId: 8, t: 6, d: 42 }, |
|
12 |
+ { jobId: 9, t: 5, d: 25 }, |
|
13 |
+ ]; |
|
14 |
+ |
|
15 |
+ const schedule = createSchedule(jobs); |
|
16 |
+ |
|
17 |
+ expect(schedule.length).toBe(9); |
|
18 |
+ expect(schedule[0]).toEqual({ t: 6, d: 10, c: 6, jobId: 1 }); |
|
19 |
+ expect(schedule[1]).toEqual({ t: 6, d: 18, c: 12, jobId: 4 }); |
|
20 |
+ expect(schedule[2]).toEqual({ t: 4, d: 20, c: 16, jobId: 3 }); |
|
21 |
+ expect(schedule[3]).toEqual({ t: 5, d: 25, c: 21, jobId: 9 }); |
|
22 |
+ expect(schedule[4]).toEqual({ t: 3, d: 35, c: 24, jobId: 7 }); |
|
23 |
+ expect(schedule[5]).toEqual({ t: 4, d: 40, c: 28, jobId: 6 }); |
|
24 |
+ expect(schedule[6]).toEqual({ t: 6, d: 42, c: 34, jobId: 8 }); |
|
25 |
+ expect(schedule[7]).toEqual({ t: 7, d: 20, c: 41, jobId: 5 }); |
|
26 |
+ expect(schedule[8]).toEqual({ t: 8, d: 25, c: 49, jobId: 2 }); |
|
27 |
+}); |
|
28 |
+ |
|
29 |
+test('create gantt', () => { |
|
30 |
+ const jobs = [ |
|
31 |
+ { jobId: 1, t: 6, d: 10 }, |
|
32 |
+ { jobId: 2, t: 8, d: 25 }, |
|
33 |
+ { jobId: 3, t: 4, d: 20 }, |
|
34 |
+ { jobId: 4, t: 6, d: 18 }, |
|
35 |
+ { jobId: 5, t: 7, d: 20 }, |
|
36 |
+ { jobId: 6, t: 4, d: 40 }, |
|
37 |
+ { jobId: 7, t: 3, d: 35 }, |
|
38 |
+ { jobId: 8, t: 6, d: 42 }, |
|
39 |
+ { jobId: 9, t: 5, d: 25 }, |
|
40 |
+ ]; |
|
41 |
+ |
|
42 |
+ const template = createGantt(jobs); |
|
43 |
+ |
|
44 |
+ expect(template.length).toBe(1); |
|
45 |
+ expect(template[0].length).toBe(9); |
|
46 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6 }); |
|
47 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 12 }); |
|
48 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 12, endTime: 16 }); |
|
49 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 16, endTime: 21 }); |
|
50 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 21, endTime: 24 }); |
|
51 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 24, endTime: 28 }); |
|
52 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 34 }); |
|
53 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 34, endTime: 41 }); |
|
54 |
+ expect(template[0][8]).toEqual({ processor: 1, startTime: 41, endTime: 49 }); |
|
55 |
+}); |
|
0 | 56 |
\ No newline at end of file |
1 | 57 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,51 @@ |
0 |
+import { |
|
1 |
+ addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
2 |
+ filter, reverse, prop, flatten, uniq, reject, contains, omit, equals, update, times |
|
3 |
+} from 'rambda'; |
|
4 |
+import type { JobId } from '../../types'; |
|
5 |
+import { updateCTime } from '../johnson'; |
|
6 |
+ |
|
7 |
+type JobOperations = { |
|
8 |
+ jobId: JobId, |
|
9 |
+ operations: Array<{ t: number }> |
|
10 |
+}; |
|
11 |
+ |
|
12 |
+type JobOperationsWithC = { |
|
13 |
+ jobId: JobId, |
|
14 |
+ operations: Array<{ t: number, c: number }> |
|
15 |
+}; |
|
16 |
+ |
|
17 |
+export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => { |
|
18 |
+ const jobsOperationsWithKoefs = map(jobsOperation => { |
|
19 |
+ const koef = reduce( |
|
20 |
+ (acc, i) => acc + Math.abs(processorsCount - 2 * i + 1) * jobsOperation.operations[i-1].t, |
|
21 |
+ 0, |
|
22 |
+ range(1, processorsCount + 1) |
|
23 |
+ ); |
|
24 |
+ |
|
25 |
+ return { ...jobsOperation, koef }; |
|
26 |
+ }, jobsOperations); |
|
27 |
+ |
|
28 |
+ let sortedJobsOperations = sort( |
|
29 |
+ (a: JobOperations, b: JobOperations) => { |
|
30 |
+ return b.koef - a.koef; |
|
31 |
+ }, |
|
32 |
+ jobsOperationsWithKoefs |
|
33 |
+ ); |
|
34 |
+ |
|
35 |
+ sortedJobsOperations = map(omit('koef'), sortedJobsOperations); |
|
36 |
+ |
|
37 |
+ return updateCTime(sortedJobsOperations, processorsCount); |
|
38 |
+}; |
|
39 |
+ |
|
40 |
+export default (jobsOperations: JobOperations[], processorsCount: number) => { |
|
41 |
+ const schedule = createSchedule(jobsOperations, processorsCount); |
|
42 |
+ |
|
43 |
+ return times(i => |
|
44 |
+ map((jobTime: JobOperationsWithC) => ({ |
|
45 |
+ processor: i + 1, |
|
46 |
+ startTime: jobTime.operations[i].c - jobTime.operations[i].t, |
|
47 |
+ endTime: jobTime.operations[i].c, |
|
48 |
+ }), schedule) |
|
49 |
+ , processorsCount); |
|
50 |
+}; |
|
0 | 51 |
\ No newline at end of file |
1 | 52 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,73 @@ |
0 |
+import createGantt, { createSchedule } from './index'; |
|
1 |
+ |
|
2 |
+test('create schedule', () => { |
|
3 |
+ const jobsWithOperations = [ |
|
4 |
+ { jobId: 1, operations: [ { t: 12 }, { t: 8 }, { t: 11 } ] }, |
|
5 |
+ { jobId: 2, operations: [ { t: 6 }, { t: 13 }, { t: 19 } ] }, |
|
6 |
+ { jobId: 3, operations: [ { t: 15 }, { t: 10 }, { t: 18 } ] }, |
|
7 |
+ { jobId: 4, operations: [ { t: 16 }, { t: 7 }, { t: 21 } ] }, |
|
8 |
+ { jobId: 5, operations: [ { t: 4 }, { t: 4 }, { t: 20 } ] }, |
|
9 |
+ { jobId: 6, operations: [ { t: 3 }, { t: 12 }, { t: 13 } ] }, |
|
10 |
+ { jobId: 7, operations: [ { t: 17 }, { t: 6 }, { t: 8 } ] }, |
|
11 |
+ { jobId: 8, operations: [ { t: 10 }, { t: 2 }, { t: 25 } ] }, |
|
12 |
+ ]; |
|
13 |
+ |
|
14 |
+ const schedule = createSchedule(jobsWithOperations, 3); |
|
15 |
+ |
|
16 |
+ expect(schedule.length).toBe(8); |
|
17 |
+ expect(schedule[0]).toEqual({ jobId: 4, operations: [ { t: 16, c: 16 }, { t: 7, c: 23 }, { t: 21, c: 44 } ] }); |
|
18 |
+ expect(schedule[1]).toEqual({ jobId: 8, operations: [ { t: 10, c: 26 }, { t: 2, c: 28 }, { t: 25, c: 69 } ] }); |
|
19 |
+ expect(schedule[2]).toEqual({ jobId: 3, operations: [ { t: 15, c: 41 }, { t: 10, c: 51 }, { t: 18, c: 87 } ] }); |
|
20 |
+ expect(schedule[3]).toEqual({ jobId: 2, operations: [ { t: 6, c: 47 }, { t: 13, c: 64 }, { t: 19, c: 106 } ] }); |
|
21 |
+ expect(schedule[4]).toEqual({ jobId: 7, operations: [ { t: 17, c: 64 }, { t: 6, c: 70 }, { t: 8, c: 114 } ] }); |
|
22 |
+ expect(schedule[5]).toEqual({ jobId: 5, operations: [ { t: 4, c: 68 }, { t: 4, c: 74 }, { t: 20, c: 134 } ] }); |
|
23 |
+ expect(schedule[6]).toEqual({ jobId: 1, operations: [ { t: 12, c: 80 }, { t: 8, c: 88 }, { t: 11, c: 145 } ] }); |
|
24 |
+ expect(schedule[7]).toEqual({ jobId: 6, operations: [ { t: 3, c: 83 }, { t: 12, c: 100 }, { t: 13, c: 158 } ] }); |
|
25 |
+}); |
|
26 |
+ |
|
27 |
+test('create gantt', () => { |
|
28 |
+ const jobsWithOperations = [ |
|
29 |
+ { jobId: 1, operations: [ { t: 12 }, { t: 8 }, { t: 11 } ] }, |
|
30 |
+ { jobId: 2, operations: [ { t: 6 }, { t: 13 }, { t: 19 } ] }, |
|
31 |
+ { jobId: 3, operations: [ { t: 15 }, { t: 10 }, { t: 18 } ] }, |
|
32 |
+ { jobId: 4, operations: [ { t: 16 }, { t: 7 }, { t: 21 } ] }, |
|
33 |
+ { jobId: 5, operations: [ { t: 4 }, { t: 4 }, { t: 20 } ] }, |
|
34 |
+ { jobId: 6, operations: [ { t: 3 }, { t: 12 }, { t: 13 } ] }, |
|
35 |
+ { jobId: 7, operations: [ { t: 17 }, { t: 6 }, { t: 8 } ] }, |
|
36 |
+ { jobId: 8, operations: [ { t: 10 }, { t: 2 }, { t: 25 } ] }, |
|
37 |
+ ]; |
|
38 |
+ |
|
39 |
+ const template = createGantt(jobsWithOperations, 3); |
|
40 |
+ |
|
41 |
+ expect(template.length).toBe(3); |
|
42 |
+ expect(template[0].length).toBe(8); |
|
43 |
+ expect(template[1].length).toBe(8); |
|
44 |
+ expect(template[2].length).toBe(8); |
|
45 |
+ |
|
46 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 16 }); |
|
47 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 16, endTime: 26 }); |
|
48 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 26, endTime: 41 }); |
|
49 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 41, endTime: 47 }); |
|
50 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 47, endTime: 64 }); |
|
51 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 64, endTime: 68 }); |
|
52 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 68, endTime: 80 }); |
|
53 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 80, endTime: 83 }); |
|
54 |
+ |
|
55 |
+ expect(template[1][0]).toEqual({ processor: 2, startTime: 16, endTime: 23 }); |
|
56 |
+ expect(template[1][1]).toEqual({ processor: 2, startTime: 26, endTime: 28 }); |
|
57 |
+ expect(template[1][2]).toEqual({ processor: 2, startTime: 41, endTime: 51 }); |
|
58 |
+ expect(template[1][3]).toEqual({ processor: 2, startTime: 51, endTime: 64 }); |
|
59 |
+ expect(template[1][4]).toEqual({ processor: 2, startTime: 64, endTime: 70 }); |
|
60 |
+ expect(template[1][5]).toEqual({ processor: 2, startTime: 70, endTime: 74 }); |
|
61 |
+ expect(template[1][6]).toEqual({ processor: 2, startTime: 80, endTime: 88 }); |
|
62 |
+ expect(template[1][7]).toEqual({ processor: 2, startTime: 88, endTime: 100 }); |
|
63 |
+ |
|
64 |
+ expect(template[2][0]).toEqual({ processor: 3, startTime: 23, endTime: 44 }); |
|
65 |
+ expect(template[2][1]).toEqual({ processor: 3, startTime: 44, endTime: 69 }); |
|
66 |
+ expect(template[2][2]).toEqual({ processor: 3, startTime: 69, endTime: 87 }); |
|
67 |
+ expect(template[2][3]).toEqual({ processor: 3, startTime: 87, endTime: 106 }); |
|
68 |
+ expect(template[2][4]).toEqual({ processor: 3, startTime: 106, endTime: 114 }); |
|
69 |
+ expect(template[2][5]).toEqual({ processor: 3, startTime: 114, endTime: 134 }); |
|
70 |
+ expect(template[2][6]).toEqual({ processor: 3, startTime: 134, endTime: 145 }); |
|
71 |
+ expect(template[2][7]).toEqual({ processor: 3, startTime: 145, endTime: 158 }); |
|
72 |
+}); |
|
0 | 73 |
\ No newline at end of file |
1 | 74 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,68 @@ |
0 |
+import { |
|
1 |
+ addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
2 |
+ filter, reverse |
|
3 |
+} from 'rambda'; |
|
4 |
+import type { JobId } from '../../types'; |
|
5 |
+import { remove, slice } from 'ramda'; |
|
6 |
+ |
|
7 |
+type JobTime = { |
|
8 |
+ jobId: JobId, |
|
9 |
+ t: number, |
|
10 |
+ d: number, |
|
11 |
+ c: number |
|
12 |
+}; |
|
13 |
+ |
|
14 |
+const findMaxTJob = (jobTimes: JobTime[]) => |
|
15 |
+ reduce( |
|
16 |
+ (maxTJob: JobTime, jobTime: JobTime) => maxTJob.t >= jobTime.t ? maxTJob : jobTime, |
|
17 |
+ { t: -Infinity }, |
|
18 |
+ jobTimes |
|
19 |
+ ); |
|
20 |
+ |
|
21 |
+const updateCTime = (jobTimes: JobTime[]) => { |
|
22 |
+ let prev = 0; |
|
23 |
+ |
|
24 |
+ const cj = map((jobTime: JobTime) => { |
|
25 |
+ prev += jobTime.t; |
|
26 |
+ |
|
27 |
+ return { ...jobTime, c: prev }; |
|
28 |
+ }, jobTimes); |
|
29 |
+ |
|
30 |
+ return cj; |
|
31 |
+}; |
|
32 |
+ |
|
33 |
+export const createSchedule = (jobs: JobTime[]) => { |
|
34 |
+ let fi = reduce((sum, job: JobTime) => sum + job.t, 0, jobs); |
|
35 |
+ let jobsD = jobs; |
|
36 |
+ let R = []; |
|
37 |
+ |
|
38 |
+ while (fi > 0) { |
|
39 |
+ const G = filter((jobTime: JobTime) => jobTime.d >= fi, jobsD); |
|
40 |
+ |
|
41 |
+ if (!G.length) return null; |
|
42 |
+ |
|
43 |
+ const maxTJob = findMaxTJob(G); |
|
44 |
+ const maxTJobIndex = findIndex(propEq('jobId', maxTJob.jobId), jobsD); |
|
45 |
+ |
|
46 |
+ jobsD = remove(maxTJobIndex, 1, jobsD); |
|
47 |
+ fi -= maxTJob.t; |
|
48 |
+ |
|
49 |
+ R.push(maxTJob); |
|
50 |
+ } |
|
51 |
+ |
|
52 |
+ const withC = updateCTime(reverse(R)); |
|
53 |
+ |
|
54 |
+ return withC; |
|
55 |
+}; |
|
56 |
+ |
|
57 |
+export default (jobs: JobTime[]) => { |
|
58 |
+ const schedule = createSchedule(jobs); |
|
59 |
+ |
|
60 |
+ const normalizedSchedule = map((jobTime: JobTime) => ({ |
|
61 |
+ processor: 1, |
|
62 |
+ startTime: jobTime.c - jobTime.t, |
|
63 |
+ endTime: jobTime.c, |
|
64 |
+ }), schedule); |
|
65 |
+ |
|
66 |
+ return [normalizedSchedule]; |
|
67 |
+}; |
|
0 | 68 |
\ No newline at end of file |
1 | 69 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,64 @@ |
0 |
+import createGantt, { createSchedule } from './index'; |
|
1 |
+ |
|
2 |
+test('create schedule', () => { |
|
3 |
+ const jobs = [ |
|
4 |
+ { jobId: 1, t: 4, d: 10 }, |
|
5 |
+ { jobId: 2, t: 6, d: 25 }, |
|
6 |
+ { jobId: 3, t: 4, d: 20 }, |
|
7 |
+ { jobId: 4, t: 6, d: 18 }, |
|
8 |
+ { jobId: 5, t: 7, d: 40 }, |
|
9 |
+ { jobId: 6, t: 4, d: 50 }, |
|
10 |
+ { jobId: 7, t: 3, d: 35 }, |
|
11 |
+ { jobId: 8, t: 2, d: 52 }, |
|
12 |
+ { jobId: 9, t: 6, d: 46 }, |
|
13 |
+ { jobId: 10, t: 7, d: 28 }, |
|
14 |
+ { jobId: 11, t: 1, d: 50 }, |
|
15 |
+ ]; |
|
16 |
+ |
|
17 |
+ const schedule = createSchedule(jobs); |
|
18 |
+ |
|
19 |
+ expect(schedule.length).toBe(11); |
|
20 |
+ expect(schedule[0]).toEqual({ jobId: 11, t: 1, d: 50, c: 1 }); |
|
21 |
+ expect(schedule[1]).toEqual({ jobId: 3, t: 4, d: 20, c: 5 }); |
|
22 |
+ expect(schedule[2]).toEqual({ jobId: 1, t: 4, d: 10, c: 9 }); |
|
23 |
+ expect(schedule[3]).toEqual({ jobId: 4, t: 6, d: 18, c: 15 }); |
|
24 |
+ expect(schedule[4]).toEqual({ jobId: 2, t: 6, d: 25, c: 21 }); |
|
25 |
+ expect(schedule[5]).toEqual({ jobId: 10, t: 7, d: 28, c: 28 }); |
|
26 |
+ expect(schedule[6]).toEqual({ jobId: 8, t: 2, d: 52, c: 30 }); |
|
27 |
+ expect(schedule[7]).toEqual({ jobId: 7, t: 3, d: 35, c: 33 }); |
|
28 |
+ expect(schedule[8]).toEqual({ jobId: 5, t: 7, d: 40, c: 40 }); |
|
29 |
+ expect(schedule[9]).toEqual({ jobId: 9, t: 6, d: 46, c: 46 }); |
|
30 |
+ expect(schedule[10]).toEqual({ jobId: 6, t: 4, d: 50, c: 50 }); |
|
31 |
+}); |
|
32 |
+ |
|
33 |
+test('create gantt', () => { |
|
34 |
+ const jobs = [ |
|
35 |
+ { jobId: 1, t: 4, d: 10 }, |
|
36 |
+ { jobId: 2, t: 6, d: 25 }, |
|
37 |
+ { jobId: 3, t: 4, d: 20 }, |
|
38 |
+ { jobId: 4, t: 6, d: 18 }, |
|
39 |
+ { jobId: 5, t: 7, d: 40 }, |
|
40 |
+ { jobId: 6, t: 4, d: 50 }, |
|
41 |
+ { jobId: 7, t: 3, d: 35 }, |
|
42 |
+ { jobId: 8, t: 2, d: 52 }, |
|
43 |
+ { jobId: 9, t: 6, d: 46 }, |
|
44 |
+ { jobId: 10, t: 7, d: 28 }, |
|
45 |
+ { jobId: 11, t: 1, d: 50 }, |
|
46 |
+ ]; |
|
47 |
+ |
|
48 |
+ const template = createGantt(jobs); |
|
49 |
+ |
|
50 |
+ expect(template.length).toBe(1); |
|
51 |
+ expect(template[0].length).toBe(11); |
|
52 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 1 }); |
|
53 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 1, endTime: 5 }); |
|
54 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 5, endTime: 9 }); |
|
55 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 9, endTime: 15 }); |
|
56 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 15, endTime: 21 }); |
|
57 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 21, endTime: 28 }); |
|
58 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 30 }); |
|
59 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 30, endTime: 33 }); |
|
60 |
+ expect(template[0][8]).toEqual({ processor: 1, startTime: 33, endTime: 40 }); |
|
61 |
+ expect(template[0][9]).toEqual({ processor: 1, startTime: 40, endTime: 46 }); |
|
62 |
+ expect(template[0][10]).toEqual({ processor: 1, startTime: 46, endTime: 50 }); |
|
63 |
+}); |
|
0 | 64 |
\ No newline at end of file |
1 | 65 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,134 @@ |
0 |
+import { |
|
1 |
+ addIndex, |
|
2 |
+ findIndex, |
|
3 |
+ find, |
|
4 |
+ map, |
|
5 |
+ propEq, |
|
6 |
+ reduce, |
|
7 |
+ sort, |
|
8 |
+ range, |
|
9 |
+ concat, |
|
10 |
+ filter, |
|
11 |
+ reverse, |
|
12 |
+ prop, |
|
13 |
+ flatten, |
|
14 |
+ uniq, |
|
15 |
+ reject, |
|
16 |
+ contains, |
|
17 |
+ omit, |
|
18 |
+ equals, |
|
19 |
+ update, |
|
20 |
+ times, |
|
21 |
+ add, |
|
22 |
+ pluck, |
|
23 |
+ last, forEach, all |
|
24 |
+} from 'rambda'; |
|
25 |
+import type { JobId } from '../../types'; |
|
26 |
+import { groupBy, max, min, remove, slice } from 'ramda'; |
|
27 |
+import { updateCTime } from '../mcnaught'; |
|
28 |
+ |
|
29 |
+type JobTime = { |
|
30 |
+ jobId: JobId, |
|
31 |
+ t: number, |
|
32 |
+ anc: JobId[] |
|
33 |
+}; |
|
34 |
+ |
|
35 |
+const noSuccJobs = (jobTimes: JobTime[]) => { |
|
36 |
+ const allAnc = flatten(map(jobTime => jobTime.ignore ? [] : jobTime.anc, jobTimes)); |
|
37 |
+ return reject(jobTime => jobTime.ignore || contains(jobTime.jobId, allAnc), jobTimes); |
|
38 |
+}; |
|
39 |
+ |
|
40 |
+const successors = (id: JobId, jobTimes: JobTime[]) => |
|
41 |
+ filter(({ anc }) => contains(id, anc), jobTimes); |
|
42 |
+ |
|
43 |
+export const createSchedule = (jobTimes: JobTime[], m: number) => { |
|
44 |
+ let T = reduce((acc, jobTime) => acc + jobTime.t, 0, jobTimes); |
|
45 |
+ |
|
46 |
+ let noSucc = noSuccJobs(jobTimes); |
|
47 |
+ |
|
48 |
+ noSucc[0].w = reduce((acc, job) => acc + job.w, 0, noSucc[0].anc) + noSucc[0].t; |
|
49 |
+ |
|
50 |
+ forEach((jobTime: JobTime) => { |
|
51 |
+ jobTime.wCalc = () => |
|
52 |
+ jobTime.t + reduce((acc, aJob) => acc + aJob.wCalc(), 0, successors(jobTime.jobId, jobTimes)) |
|
53 |
+ }, jobTimes); |
|
54 |
+ |
|
55 |
+ forEach((jobTime: JobTime) => { |
|
56 |
+ jobTime.w = jobTime.wCalc() |
|
57 |
+ }, jobTimes); |
|
58 |
+ |
|
59 |
+ let c = Math.floor(T / m) + Math.sign(T - m * Math.floor(T / m)); |
|
60 |
+ let assignedIds; |
|
61 |
+ let jobTimesSorted = sort((a, b) => b.w - a.w, jobTimes); |
|
62 |
+ |
|
63 |
+ while (c <= T) { |
|
64 |
+ assignedIds = []; |
|
65 |
+ let activeProcessor = 1; |
|
66 |
+ // console.log('procesor', activeProcessor); |
|
67 |
+ |
|
68 |
+ while (activeProcessor <= m) { |
|
69 |
+ let restC = c; |
|
70 |
+ let lastActiveProcessor = activeProcessor; |
|
71 |
+ |
|
72 |
+ forEach(withMaxW => { |
|
73 |
+ if (contains(withMaxW.jobId, assignedIds)) { |
|
74 |
+ // console.log('already assigned', withMaxW.jobId); |
|
75 |
+ return; |
|
76 |
+ } |
|
77 |
+ |
|
78 |
+ let wereAllAncAssigned = all(jobId => contains(jobId, assignedIds), withMaxW.anc); |
|
79 |
+ |
|
80 |
+ if (!wereAllAncAssigned) { |
|
81 |
+ // console.log('nemozem priradit', withMaxW.jobId); |
|
82 |
+ return; |
|
83 |
+ } |
|
84 |
+ |
|
85 |
+ if (withMaxW.t <= restC) { |
|
86 |
+ withMaxW.processor = activeProcessor; |
|
87 |
+ assignedIds.push(withMaxW.jobId); |
|
88 |
+ restC -= withMaxW.t; |
|
89 |
+ // console.log('priradil som', withMaxW.jobId); |
|
90 |
+ } else { |
|
91 |
+ // console.log('nezmesti sa', withMaxW.jobId); |
|
92 |
+ } |
|
93 |
+ }, jobTimesSorted); |
|
94 |
+ |
|
95 |
+ if (!restC || find(jobTime => jobTime.t <= restC && !contains(jobTime.jobId, assignedIds), jobTimesSorted)) { |
|
96 |
+ activeProcessor++; |
|
97 |
+ // console.log('dalsi procesor', activeProcessor); |
|
98 |
+ } |
|
99 |
+ |
|
100 |
+ if (activeProcessor === lastActiveProcessor) { |
|
101 |
+ // console.log('zvys takt'); |
|
102 |
+ break; |
|
103 |
+ } |
|
104 |
+ } |
|
105 |
+ |
|
106 |
+ if (assignedIds.length === jobTimesSorted.length) { |
|
107 |
+ break; |
|
108 |
+ } |
|
109 |
+ |
|
110 |
+ // console.log('dalsi takt', c+1); |
|
111 |
+ c++; |
|
112 |
+ } |
|
113 |
+ |
|
114 |
+ if (assignedIds.length !== jobTimesSorted.length) { |
|
115 |
+ return null; |
|
116 |
+ } |
|
117 |
+ |
|
118 |
+ return updateCTime( |
|
119 |
+ map(omit(['wCalc', 'anc', 'w']), jobTimesSorted) |
|
120 |
+ ); |
|
121 |
+}; |
|
122 |
+ |
|
123 |
+export default (jobTimes: JobTime[], processorsCount: number) => { |
|
124 |
+ const schedule = createSchedule(jobTimes, processorsCount); |
|
125 |
+ |
|
126 |
+ const grouped = Object.values(groupBy(jobTime => jobTime.processor, schedule)); |
|
127 |
+ |
|
128 |
+ return map(map(job => ({ |
|
129 |
+ processor: job.processor, |
|
130 |
+ startTime: job.c - job.t, |
|
131 |
+ endTime: job.c |
|
132 |
+ })), grouped); |
|
133 |
+}; |
|
0 | 134 |
\ No newline at end of file |
1 | 135 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,76 @@ |
0 |
+import createGantt, { createSchedule } from './index'; |
|
1 |
+ |
|
2 |
+test('create schedule 1', () => { |
|
3 |
+ const jobsWithOperations = [ |
|
4 |
+ { jobId: 1, t: 4, anc: [] }, |
|
5 |
+ { jobId: 2, t: 6, anc: [1] }, |
|
6 |
+ { jobId: 3, t: 7, anc: [1] }, |
|
7 |
+ { jobId: 4, t: 6, anc: [1] }, |
|
8 |
+ { jobId: 5, t: 4, anc: [2, 3] }, |
|
9 |
+ { jobId: 6, t: 7, anc: [4, 5] }, |
|
10 |
+ { jobId: 7, t: 2, anc: [2, 4, 6] }, |
|
11 |
+ ]; |
|
12 |
+ |
|
13 |
+ const schedule = createSchedule(jobsWithOperations, 2); |
|
14 |
+ |
|
15 |
+ expect(schedule.length).toBe(7); |
|
16 |
+ expect(schedule[0]).toEqual({ jobId: 1, t: 4, c: 4, processor: 1 }); |
|
17 |
+ expect(schedule[1]).toEqual({ jobId: 2, t: 6, c: 10, processor: 1 }); |
|
18 |
+ expect(schedule[2]).toEqual({ jobId: 3, t: 7, c: 17, processor: 1 }); |
|
19 |
+ expect(schedule[3]).toEqual({ jobId: 4, t: 6, c: 6, processor: 2 }); |
|
20 |
+ expect(schedule[4]).toEqual({ jobId: 5, t: 4, c: 10, processor: 2 }); |
|
21 |
+ expect(schedule[5]).toEqual({ jobId: 6, t: 7, c: 17, processor: 2 }); |
|
22 |
+ expect(schedule[6]).toEqual({ jobId: 7, t: 2, c: 19, processor: 2 }); |
|
23 |
+}); |
|
24 |
+ |
|
25 |
+test('create schedule 2', () => { |
|
26 |
+ const jobsWithOperations = [ |
|
27 |
+ { jobId: 1, t: 4, anc: [] }, |
|
28 |
+ { jobId: 2, t: 6, anc: [1] }, |
|
29 |
+ { jobId: 3, t: 7, anc: [1] }, |
|
30 |
+ { jobId: 4, t: 6, anc: [1] }, |
|
31 |
+ { jobId: 5, t: 4, anc: [2, 3] }, |
|
32 |
+ { jobId: 6, t: 7, anc: [4, 5] }, |
|
33 |
+ { jobId: 7, t: 2, anc: [2, 4, 6] }, |
|
34 |
+ ]; |
|
35 |
+ |
|
36 |
+ const schedule = createSchedule(jobsWithOperations, 3); |
|
37 |
+ |
|
38 |
+ expect(schedule.length).toBe(7); |
|
39 |
+ expect(schedule[0]).toEqual({ jobId: 1, t: 4, c: 4, processor: 1 }); |
|
40 |
+ expect(schedule[1]).toEqual({ jobId: 2, t: 6, c: 10, processor: 1 }); |
|
41 |
+ expect(schedule[2]).toEqual({ jobId: 3, t: 7, c: 7, processor: 2 }); |
|
42 |
+ expect(schedule[3]).toEqual({ jobId: 4, t: 6, c: 13, processor: 2 }); |
|
43 |
+ expect(schedule[4]).toEqual({ jobId: 5, t: 4, c: 4, processor: 3 }); |
|
44 |
+ expect(schedule[5]).toEqual({ jobId: 6, t: 7, c: 11, processor: 3 }); |
|
45 |
+ expect(schedule[6]).toEqual({ jobId: 7, t: 2, c: 13, processor: 3 }); |
|
46 |
+}); |
|
47 |
+ |
|
48 |
+test('create gantt', () => { |
|
49 |
+ const jobsWithOperations = [ |
|
50 |
+ { jobId: 1, t: 4, anc: [] }, |
|
51 |
+ { jobId: 2, t: 6, anc: [1] }, |
|
52 |
+ { jobId: 3, t: 7, anc: [1] }, |
|
53 |
+ { jobId: 4, t: 6, anc: [1] }, |
|
54 |
+ { jobId: 5, t: 4, anc: [2, 3] }, |
|
55 |
+ { jobId: 6, t: 7, anc: [4, 5] }, |
|
56 |
+ { jobId: 7, t: 2, anc: [2, 4, 6] }, |
|
57 |
+ ]; |
|
58 |
+ |
|
59 |
+ const template = createGantt(jobsWithOperations, 3); |
|
60 |
+ |
|
61 |
+ expect(template.length).toBe(3); |
|
62 |
+ expect(template[0].length).toBe(2); |
|
63 |
+ expect(template[1].length).toBe(2); |
|
64 |
+ expect(template[2].length).toBe(3); |
|
65 |
+ |
|
66 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 4 }); |
|
67 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 4, endTime: 10 }); |
|
68 |
+ |
|
69 |
+ expect(template[1][0]).toEqual({ processor: 2, startTime: 0, endTime: 7 }); |
|
70 |
+ expect(template[1][1]).toEqual({ processor: 2, startTime: 7, endTime: 13 }); |
|
71 |
+ |
|
72 |
+ expect(template[2][0]).toEqual({ processor: 3, startTime: 0, endTime: 4 }); |
|
73 |
+ expect(template[2][1]).toEqual({ processor: 3, startTime: 4, endTime: 11 }); |
|
74 |
+ expect(template[2][2]).toEqual({ processor: 3, startTime: 11, endTime: 13 }); |
|
75 |
+}); |
|
0 | 76 |
\ No newline at end of file |