| ... | ... |
@@ -2,41 +2,97 @@ import React, { PureComponent } from 'react';
|
| 2 | 2 |
import Form from './components/Form'; |
| 3 | 3 |
import Gantt from './components/Gantt'; |
| 4 | 4 |
import { normalizeData } from './ganttUtils';
|
| 5 |
+import algorithms from './algorithms'; |
|
| 6 |
+import { filter, flatten, identity, map, pluck, range, times } from 'rambda';
|
|
| 7 |
+import uuid from 'uuid/v4'; |
|
| 8 |
+import { selectAlgorithms, fastestResult, najneomeskanejsiResult } from './selectAlgorithm';
|
|
| 5 | 9 |
|
| 6 |
-const algoData = [ |
|
| 7 |
- [ |
|
| 8 |
- { startTime: 1, endTime: 3, processor: 1, name: 'UI dizajnér' },
|
|
| 9 |
- { startTime: 4, endTime: 6, processor: 1, name: 'UX dizajnér' },
|
|
| 10 |
- { startTime: 6, endTime: 10, processor: 1, name: 'UI dizajnér' },
|
|
| 11 |
- ], |
|
| 12 |
- [ |
|
| 13 |
- { startTime: 6, endTime: 10, processor: 2, name: 'UI dizajnér' },
|
|
| 14 |
- { startTime: 22, endTime: 23, processor: 2, name: 'UI dizajnér' },
|
|
| 15 |
- { startTime: 23, endTime: 24, processor: 2, name: 'UI dizajnér' },
|
|
| 16 |
- { startTime: 24, endTime: 25, processor: 2, name: 'UI dizajnér' },
|
|
| 17 |
- ], |
|
| 18 |
- [ |
|
| 19 |
- { startTime: 10, endTime: 16, processor: 3, name: 'UI dizajnér' },
|
|
| 20 |
- ], |
|
| 21 |
- [ |
|
| 22 |
- { startTime: 10, endTime: 16, processor: 4, name: 'UI dizajnér' },
|
|
| 23 |
- ], |
|
| 24 |
- [ |
|
| 25 |
- { startTime: 10, endTime: 16, processor: 5, name: 'UI dizajnér' },
|
|
| 26 |
- ], |
|
| 27 |
- [ |
|
| 28 |
- { startTime: 10, endTime: 16, processor: 6, name: 'UI dizajnér' },
|
|
| 29 |
- ] |
|
| 30 |
-]; |
|
| 31 |
- |
|
| 32 |
-const data = normalizeData(algoData); |
|
| 10 |
+// const algoData = [ |
|
| 11 |
+// [ |
|
| 12 |
+// { startTime: 1, endTime: 3, processor: 1, name: 'UI dizajnér' },
|
|
| 13 |
+// { startTime: 4, endTime: 6, processor: 1, name: 'UX dizajnér' },
|
|
| 14 |
+// { startTime: 6, endTime: 10, processor: 1, name: 'UI dizajnér' },
|
|
| 15 |
+// ] |
|
| 16 |
+// ]; |
|
| 17 |
+// |
|
| 18 |
+// const data = normalizeData(algoData); |
|
| 33 | 19 |
|
| 34 | 20 |
class App extends PureComponent {
|
| 21 |
+ state = { ganttData: null };
|
|
| 22 |
+ |
|
| 23 |
+ onFormSubmit = ({ preempt, rows }) => {
|
|
| 24 |
+ const algorithmNames = selectAlgorithms(rows, preempt); |
|
| 25 |
+ |
|
| 26 |
+ if (!algorithmNames) {
|
|
| 27 |
+ alert('Pre zvolenú konfiguráciu sa nenašiel vhodný algoritmus');
|
|
| 28 |
+ return; |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ const results = map(algorithmName => {
|
|
| 32 |
+ const algorithm = algorithms[algorithmName]; |
|
| 33 |
+ |
|
| 34 |
+ let jobTimes; |
|
| 35 |
+ |
|
| 36 |
+ if (algorithmName === 'johnson' || algorithmName === 'palmer' || algorithmName === 'grupt' || algorithmName === 'campbel') {
|
|
| 37 |
+ jobTimes = times(i => ({
|
|
| 38 |
+ jobId: uuid(), |
|
| 39 |
+ operations: map(j => ({ t: rows[j].jobs[i].t }), range(0, rows.length))
|
|
| 40 |
+ }), rows[0].jobs.length); |
|
| 41 |
+ } else if (algorithmName === 'mcnaught' || algorithmName === 'vahy') {
|
|
| 42 |
+ jobTimes = flatten(pluck('jobs', rows));
|
|
| 43 |
+ jobTimes = map(jobTime => ({
|
|
| 44 |
+ ...jobTime, |
|
| 45 |
+ jobId: jobTime.id, |
|
| 46 |
+ anc: map(anc => anc.split(':')[1], jobTime.anc || []) }), jobTimes);
|
|
| 47 |
+ } else {
|
|
| 48 |
+ jobTimes = map(job => ({
|
|
| 49 |
+ jobId: job.id, |
|
| 50 |
+ t: +job.t, |
|
| 51 |
+ d: +job.d, |
|
| 52 |
+ w: +job.w, |
|
| 53 |
+ name: job.name, |
|
| 54 |
+ anc: map(anc => anc.split(':')[1], job.anc || [])
|
|
| 55 |
+ }), rows[0].jobs); |
|
| 56 |
+ } |
|
| 57 |
+ |
|
| 58 |
+ // console.log(jobTimes); |
|
| 59 |
+ // return; |
|
| 60 |
+ |
|
| 61 |
+ // console.log(rows[1].jobs); |
|
| 62 |
+ |
|
| 63 |
+ const result = algorithm(jobTimes, rows.length); |
|
| 64 |
+ return result; |
|
| 65 |
+ }, algorithmNames); |
|
| 66 |
+ |
|
| 67 |
+ const validResults = filter(identity, results); |
|
| 68 |
+ const fastestIndex = rows.length === 1 |
|
| 69 |
+ ? najneomeskanejsiResult(validResults) |
|
| 70 |
+ : fastestResult(validResults); |
|
| 71 |
+ |
|
| 72 |
+ if (fastestIndex === -1) {
|
|
| 73 |
+ alert('Nepodarilo sa násť rozvrh pre zvolenú konfiguráciu');
|
|
| 74 |
+ return; |
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ // console.log(fastestIndex, validResults); |
|
| 78 |
+ |
|
| 79 |
+ const ganttData = normalizeData(validResults[fastestIndex]); |
|
| 80 |
+ |
|
| 81 |
+ console.log(validResults[fastestIndex]); |
|
| 82 |
+ |
|
| 83 |
+ this.setState({
|
|
| 84 |
+ ganttData |
|
| 85 |
+ }); |
|
| 86 |
+ }; |
|
| 87 |
+ |
|
| 35 | 88 |
render() {
|
| 89 |
+ const { ganttData } = this.state;
|
|
| 90 |
+ |
|
| 36 | 91 |
return ( |
| 37 | 92 |
<div style={{ padding: 20 }}>
|
| 38 |
- <Form /> |
|
| 39 |
- <Gantt data={data} />
|
|
| 93 |
+ <Form onSubmit={this.onFormSubmit} />
|
|
| 94 |
+ {ganttData && <Gantt data={ganttData}/>}
|
|
| 95 |
+ {/*<Gantt data={data} />*/}
|
|
| 40 | 96 |
</div> |
| 41 | 97 |
); |
| 42 | 98 |
} |
| ... | ... |
@@ -1,31 +1,16 @@ |
| 1 | 1 |
import {
|
| 2 | 2 |
addIndex, |
| 3 |
- findIndex, |
|
| 4 | 3 |
find, |
| 5 | 4 |
map, |
| 6 | 5 |
propEq, |
| 7 | 6 |
reduce, |
| 8 |
- sort, |
|
| 9 | 7 |
range, |
| 10 |
- concat, |
|
| 11 |
- filter, |
|
| 12 |
- reverse, |
|
| 13 |
- prop, |
|
| 14 |
- flatten, |
|
| 15 |
- uniq, |
|
| 16 |
- reject, |
|
| 17 |
- contains, |
|
| 18 |
- omit, |
|
| 19 |
- equals, |
|
| 20 |
- update, |
|
| 21 | 8 |
times, |
| 22 |
- add, |
|
| 23 |
- pluck, |
|
| 24 | 9 |
last |
| 25 | 10 |
} from 'rambda'; |
| 26 | 11 |
import type { JobId } from '../../types';
|
| 27 |
-import { max, min, remove, slice } from 'ramda';
|
|
| 28 | 12 |
import { createSchedule as johnson, updateCTime } from '../johnson';
|
| 13 |
+import { max } from 'ramda';
|
|
| 29 | 14 |
|
| 30 | 15 |
type JobOperations = {
|
| 31 | 16 |
jobId: JobId, |
| ... | ... |
@@ -37,7 +22,26 @@ type JobOperationsWithC = {
|
| 37 | 37 |
operations: Array<{ t: number, c: number }>
|
| 38 | 38 |
}; |
| 39 | 39 |
|
| 40 |
+export const fillCorrupted = (jobsOperations: JobOperations[]) => {
|
|
| 41 |
+ const maxOperationsCount = reduce((acc, { operations }) => max(operations.length, acc), 0, jobsOperations);
|
|
| 42 |
+ |
|
| 43 |
+ return map( |
|
| 44 |
+ ({ operations, ...rest }) => {
|
|
| 45 |
+ const fixedOperations = [...operations]; |
|
| 46 |
+ fixedOperations.length = maxOperationsCount; |
|
| 47 |
+ |
|
| 48 |
+ return {
|
|
| 49 |
+ ...rest, |
|
| 50 |
+ operations: fixedOperations.fill({ t: 0 }, operations.length, maxOperationsCount)
|
|
| 51 |
+ }; |
|
| 52 |
+ }, |
|
| 53 |
+ jobsOperations |
|
| 54 |
+ ) |
|
| 55 |
+}; |
|
| 56 |
+ |
|
| 40 | 57 |
export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => {
|
| 58 |
+ jobsOperations = fillCorrupted(jobsOperations); |
|
| 59 |
+ |
|
| 41 | 60 |
const allSchedules = map(k => {
|
| 42 | 61 |
const jobsOperationsForK = map((jobsOperation: JobOperations) => {
|
| 43 | 62 |
const t1 = reduce((acc, i) => acc + jobsOperation.operations[i].t, 0, range(0, k)); |
| ... | ... |
@@ -2,6 +2,7 @@ import { map, reduce, sort, range, omit, times } from 'rambda';
|
| 2 | 2 |
import type { JobId } from '../../types';
|
| 3 | 3 |
import { updateCTime } from '../johnson';
|
| 4 | 4 |
import { min } from 'ramda';
|
| 5 |
+import { fillCorrupted } from '../campbel';
|
|
| 5 | 6 |
|
| 6 | 7 |
type JobOperations = {
|
| 7 | 8 |
jobId: JobId, |
| ... | ... |
@@ -14,6 +15,8 @@ type JobOperationsWithC = {
|
| 14 | 14 |
}; |
| 15 | 15 |
|
| 16 | 16 |
export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => {
|
| 17 |
+ jobsOperations = fillCorrupted(jobsOperations); |
|
| 18 |
+ |
|
| 17 | 19 |
const jobsOperationsWithKoefs = map((jobsOperation: JobOperations) => {
|
| 18 | 20 |
const ej = jobsOperation.operations[0].t < jobsOperation.operations[processorsCount - 1].t |
| 19 | 21 |
? 1 : -1; |
| 20 | 22 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,21 @@ |
| 0 |
+import campbel from './campbel'; |
|
| 1 |
+import grupt from './grupt'; |
|
| 2 |
+import johnson from './johnson'; |
|
| 3 |
+import lawler from './lawler'; |
|
| 4 |
+import mcnaught from './mcnaught'; |
|
| 5 |
+import moore from './moore'; |
|
| 6 |
+import palmer from './palmer'; |
|
| 7 |
+import smith from './smith'; |
|
| 8 |
+import vahy from './vahy'; |
|
| 9 |
+ |
|
| 10 |
+export default {
|
|
| 11 |
+ campbel, |
|
| 12 |
+ grupt, |
|
| 13 |
+ johnson, |
|
| 14 |
+ lawler, |
|
| 15 |
+ mcnaught, |
|
| 16 |
+ moore, |
|
| 17 |
+ palmer, |
|
| 18 |
+ smith, |
|
| 19 |
+ vahy |
|
| 20 |
+}; |
|
| 0 | 21 |
\ No newline at end of file |
| ... | ... |
@@ -1,18 +1,19 @@ |
| 1 | 1 |
import {
|
| 2 |
- addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
| 3 |
- filter, reverse, prop, flatten, uniq, reject, contains, omit, equals, update, times |
|
| 2 |
+ findIndex, map, reduce, sort, concat, |
|
| 3 |
+ filter, reverse, equals, update, times |
|
| 4 | 4 |
} from 'rambda'; |
| 5 | 5 |
import type { JobId } from '../../types';
|
| 6 |
-import { max, min, remove, slice } from 'ramda';
|
|
| 6 |
+import { remove } from 'ramda';
|
|
| 7 |
+import { fillCorrupted } from '../campbel';
|
|
| 7 | 8 |
|
| 8 | 9 |
type JobOperations = {
|
| 9 | 10 |
jobId: JobId, |
| 10 |
- operations: Array<{ t: number }>
|
|
| 11 |
+ operations: [{ t: number }, { t: number }]
|
|
| 11 | 12 |
}; |
| 12 | 13 |
|
| 13 | 14 |
type JobOperationsWithC = {
|
| 14 | 15 |
jobId: JobId, |
| 15 |
- operations: Array<{ t: number, c: number }>
|
|
| 16 |
+ operations: [{ t: number, c: number }, { t: number, c: number }]
|
|
| 16 | 17 |
}; |
| 17 | 18 |
|
| 18 | 19 |
const findMinT = (operations) => |
| ... | ... |
@@ -72,6 +73,8 @@ export const updateCTime = (jobsOperations: JobOperations[], processorCount: num |
| 72 | 72 |
}; |
| 73 | 73 |
|
| 74 | 74 |
export const createSchedule = (jobsOperations: JobOperations[]): JobOperationsWithC[] => {
|
| 75 |
+ jobsOperations = fillCorrupted(jobsOperations); |
|
| 76 |
+ |
|
| 75 | 77 |
let I = jobsOperations; |
| 76 | 78 |
let L = [[], []]; |
| 77 | 79 |
|
| ... | ... |
@@ -1,26 +1,18 @@ |
| 1 | 1 |
import {
|
| 2 |
- addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
| 3 |
- filter, reverse, prop, flatten, uniq, reject, contains, omit |
|
| 2 |
+ findIndex, map, propEq, reduce, |
|
| 3 |
+ reverse, prop, flatten, uniq, reject, contains, omit |
|
| 4 | 4 |
} from 'rambda'; |
| 5 | 5 |
import type { JobId } from '../../types';
|
| 6 |
-import { max, remove, slice } from 'ramda';
|
|
| 6 |
+import { max, remove } from 'ramda';
|
|
| 7 | 7 |
|
| 8 | 8 |
type JobTime = {
|
| 9 | 9 |
jobId: JobId, |
| 10 | 10 |
t: number, |
| 11 | 11 |
d: number, |
| 12 |
- c: number, |
|
| 13 | 12 |
w: number, |
| 14 | 13 |
anc: JobId[], |
| 15 | 14 |
}; |
| 16 | 15 |
|
| 17 |
-const findMaxTJob = (jobTimes: JobTime[]) => |
|
| 18 |
- reduce( |
|
| 19 |
- (maxTJob: JobTime, jobTime: JobTime) => maxTJob.t >= jobTime.t ? maxTJob : jobTime, |
|
| 20 |
- { t: -Infinity },
|
|
| 21 |
- jobTimes |
|
| 22 |
- ); |
|
| 23 |
- |
|
| 24 | 16 |
const findMinJob = (fi: number, jobTimes: JobTime[]) => |
| 25 | 17 |
reduce( |
| 26 | 18 |
(minJob: JobTime, jobTime: JobTime) => {
|
| ... | ... |
@@ -45,7 +37,13 @@ const updateCTime = (jobTimes: JobTime[]) => {
|
| 45 | 45 |
return cj; |
| 46 | 46 |
}; |
| 47 | 47 |
|
| 48 |
+const fillCorrupted = (jobs: JobTime[]) => map( |
|
| 49 |
+ ({ d, w, anc, ...rest }) => ({ ...rest, d: d || 1000, w: w || 1, anc: anc || [] }),
|
|
| 50 |
+ jobs); |
|
| 51 |
+ |
|
| 48 | 52 |
export const createSchedule = (jobs: JobTime[]) => {
|
| 53 |
+ jobs = fillCorrupted(jobs); |
|
| 54 |
+ |
|
| 49 | 55 |
let fi = reduce((sum, job: JobTime) => sum + job.t, 0, jobs); |
| 50 | 56 |
let jobsD = jobs; |
| 51 | 57 |
let R = []; |
| ... | ... |
@@ -76,10 +74,13 @@ export const createSchedule = (jobs: JobTime[]) => {
|
| 76 | 76 |
export default (jobs: JobTime[]) => {
|
| 77 | 77 |
const schedule = createSchedule(jobs); |
| 78 | 78 |
|
| 79 |
+ if (!schedule) return null; |
|
| 80 |
+ |
|
| 79 | 81 |
const normalizedSchedule = map((jobTime: JobTime) => ({
|
| 80 | 82 |
processor: 1, |
| 81 | 83 |
startTime: jobTime.c - jobTime.t, |
| 82 | 84 |
endTime: jobTime.c, |
| 85 |
+ delayed: max(0, jobTime.c - jobTime.d) |
|
| 83 | 86 |
}), schedule); |
| 84 | 87 |
|
| 85 | 88 |
return [normalizedSchedule]; |
| ... | ... |
@@ -50,15 +50,15 @@ test('create gantt', () => {
|
| 50 | 50 |
|
| 51 | 51 |
expect(template.length).toBe(1); |
| 52 | 52 |
expect(template[0].length).toBe(11); |
| 53 |
- expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6 });
|
|
| 54 |
- expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 14 });
|
|
| 55 |
- expect(template[0][2]).toEqual({ processor: 1, startTime: 14, endTime: 19 });
|
|
| 56 |
- expect(template[0][3]).toEqual({ processor: 1, startTime: 19, endTime: 24 });
|
|
| 57 |
- expect(template[0][4]).toEqual({ processor: 1, startTime: 24, endTime: 27 });
|
|
| 58 |
- expect(template[0][5]).toEqual({ processor: 1, startTime: 27, endTime: 36 });
|
|
| 59 |
- expect(template[0][6]).toEqual({ processor: 1, startTime: 36, endTime: 45 });
|
|
| 60 |
- expect(template[0][7]).toEqual({ processor: 1, startTime: 45, endTime: 50 });
|
|
| 61 |
- expect(template[0][8]).toEqual({ processor: 1, startTime: 50, endTime: 58 });
|
|
| 62 |
- expect(template[0][9]).toEqual({ processor: 1, startTime: 58, endTime: 63 });
|
|
| 63 |
- expect(template[0][10]).toEqual({ processor: 1, startTime: 63, endTime: 68 });
|
|
| 53 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6, delayed: 0 });
|
|
| 54 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 14, delayed: 3 });
|
|
| 55 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 14, endTime: 19, delayed: 5 });
|
|
| 56 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 19, endTime: 24, delayed: 15 });
|
|
| 57 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 24, endTime: 27, delayed: 13 });
|
|
| 58 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 27, endTime: 36, delayed: 17 });
|
|
| 59 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 36, endTime: 45, delayed: 9 });
|
|
| 60 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 45, endTime: 50, delayed: 11 });
|
|
| 61 |
+ expect(template[0][8]).toEqual({ processor: 1, startTime: 50, endTime: 58, delayed: 27 });
|
|
| 62 |
+ expect(template[0][9]).toEqual({ processor: 1, startTime: 58, endTime: 63, delayed: 2 });
|
|
| 63 |
+ expect(template[0][10]).toEqual({ processor: 1, startTime: 63, endTime: 68, delayed: 9 });
|
|
| 64 | 64 |
}); |
| 65 | 65 |
\ No newline at end of file |
| ... | ... |
@@ -1,9 +1,6 @@ |
| 1 |
-import {
|
|
| 2 |
- addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
| 3 |
- filter, reverse, prop, flatten, uniq, reject, contains, omit, times |
|
| 4 |
-} from 'rambda'; |
|
| 1 |
+import { map, reduce, times } from 'rambda';
|
|
| 5 | 2 |
import type { JobId } from '../../types';
|
| 6 |
-import { groupBy, max, remove, slice } from 'ramda';
|
|
| 3 |
+import { groupBy, max } from 'ramda';
|
|
| 7 | 4 |
|
| 8 | 5 |
type JobTime = {
|
| 9 | 6 |
jobId: JobId, |
| ... | ... |
@@ -46,8 +43,6 @@ export const createSchedule = (jobs: JobTime[], processorCount: number): JobTime |
| 46 | 46 |
let processorC = times(() => 0, processorCount + 1); |
| 47 | 47 |
let activeProcessor = 1; |
| 48 | 48 |
|
| 49 |
- let i = 0; |
|
| 50 |
- |
|
| 51 | 49 |
while (G.length) {
|
| 52 | 50 |
if (processorC[activeProcessor] + G[0].t <= K) {
|
| 53 | 51 |
processorC[activeProcessor] += G[0].t; |
| ... | ... |
@@ -65,8 +60,6 @@ export const createSchedule = (jobs: JobTime[], processorCount: number): JobTime |
| 65 | 65 |
|
| 66 | 66 |
activeProcessor++; |
| 67 | 67 |
} |
| 68 |
- |
|
| 69 |
- i++; |
|
| 70 | 68 |
} |
| 71 | 69 |
|
| 72 | 70 |
const withC = updateCTime(R); |
| ... | ... |
@@ -1,21 +1,13 @@ |
| 1 |
-import { addIndex, findIndex, find, map, propEq, reduce, sort, range, concat } from 'rambda';
|
|
| 1 |
+import { findIndex, find, map, propEq, reduce, sort, concat } from 'rambda';
|
|
| 2 | 2 |
import type { JobId } from '../../types';
|
| 3 |
-import { remove, slice } from 'ramda';
|
|
| 3 |
+import { max, remove, slice } from 'ramda';
|
|
| 4 | 4 |
|
| 5 | 5 |
type JobTime = {
|
| 6 | 6 |
jobId: JobId, |
| 7 | 7 |
t: number, |
| 8 | 8 |
d: number, |
| 9 |
- c: number |
|
| 10 | 9 |
}; |
| 11 | 10 |
|
| 12 |
-const mapWithIndex = addIndex(map); |
|
| 13 |
- |
|
| 14 |
-// const byAsc = (a: JobTime, b: JobTime) => a.time - b.time; |
|
| 15 |
- |
|
| 16 |
-const findJobById = (jobId: JobId, jobTimes: JobTime[]): JobTime => |
|
| 17 |
- find(propEq('jobId', jobId), jobTimes);
|
|
| 18 |
- |
|
| 19 | 11 |
const findLateJob = (jobTimes: JobTime[]) => |
| 20 | 12 |
find(({ c, d }: JobTime) => c > d, jobTimes);
|
| 21 | 13 |
|
| ... | ... |
@@ -38,7 +30,13 @@ const updateCTime = (jobTimes: JobTime[]) => {
|
| 38 | 38 |
return cj; |
| 39 | 39 |
}; |
| 40 | 40 |
|
| 41 |
+export const fillCorrupted = (jobs: JobTime[]) => map( |
|
| 42 |
+ ({ d, ...rest }) => ({ ...rest, d: d || 1000 }),
|
|
| 43 |
+ jobs); |
|
| 44 |
+ |
|
| 41 | 45 |
export const createSchedule = (jobs: JobTime[]) => {
|
| 46 |
+ jobs = fillCorrupted(jobs); |
|
| 47 |
+ |
|
| 42 | 48 |
const F = sort((a: JobTime, b: JobTime) => a.d - b.d, jobs); |
| 43 | 49 |
const L = []; |
| 44 | 50 |
|
| ... | ... |
@@ -70,6 +68,7 @@ export default (jobs: JobTime[]) => {
|
| 70 | 70 |
processor: 1, |
| 71 | 71 |
startTime: jobTime.c - jobTime.t, |
| 72 | 72 |
endTime: jobTime.c, |
| 73 |
+ delayed: max(0, jobTime.c - jobTime.d) |
|
| 73 | 74 |
}), schedule); |
| 74 | 75 |
|
| 75 | 76 |
return [normalizedSchedule]; |
| ... | ... |
@@ -44,13 +44,13 @@ test('create gantt', () => {
|
| 44 | 44 |
|
| 45 | 45 |
expect(template.length).toBe(1); |
| 46 | 46 |
expect(template[0].length).toBe(9); |
| 47 |
- expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6 });
|
|
| 48 |
- expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 12 });
|
|
| 49 |
- expect(template[0][2]).toEqual({ processor: 1, startTime: 12, endTime: 16 });
|
|
| 50 |
- expect(template[0][3]).toEqual({ processor: 1, startTime: 16, endTime: 21 });
|
|
| 51 |
- expect(template[0][4]).toEqual({ processor: 1, startTime: 21, endTime: 24 });
|
|
| 52 |
- expect(template[0][5]).toEqual({ processor: 1, startTime: 24, endTime: 28 });
|
|
| 53 |
- expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 34 });
|
|
| 54 |
- expect(template[0][7]).toEqual({ processor: 1, startTime: 34, endTime: 41 });
|
|
| 55 |
- expect(template[0][8]).toEqual({ processor: 1, startTime: 41, endTime: 49 });
|
|
| 47 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6, delayed: 0 });
|
|
| 48 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 12, delayed: 0 });
|
|
| 49 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 12, endTime: 16, delayed: 0 });
|
|
| 50 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 16, endTime: 21, delayed: 0 });
|
|
| 51 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 21, endTime: 24, delayed: 0 });
|
|
| 52 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 24, endTime: 28, delayed: 0 });
|
|
| 53 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 34, delayed: 0 });
|
|
| 54 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 34, endTime: 41, delayed: 21 });
|
|
| 55 |
+ expect(template[0][8]).toEqual({ processor: 1, startTime: 41, endTime: 49, delayed: 24 });
|
|
| 56 | 56 |
}); |
| 57 | 57 |
\ No newline at end of file |
| ... | ... |
@@ -1,9 +1,9 @@ |
| 1 | 1 |
import {
|
| 2 |
- addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
| 3 |
- filter, reverse, prop, flatten, uniq, reject, contains, omit, equals, update, times |
|
| 2 |
+ map, reduce, sort, range, omit, times |
|
| 4 | 3 |
} from 'rambda'; |
| 5 | 4 |
import type { JobId } from '../../types';
|
| 6 | 5 |
import { updateCTime } from '../johnson';
|
| 6 |
+import { fillCorrupted } from '../campbel';
|
|
| 7 | 7 |
|
| 8 | 8 |
type JobOperations = {
|
| 9 | 9 |
jobId: JobId, |
| ... | ... |
@@ -16,6 +16,8 @@ type JobOperationsWithC = {
|
| 16 | 16 |
}; |
| 17 | 17 |
|
| 18 | 18 |
export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => {
|
| 19 |
+ jobsOperations = fillCorrupted(jobsOperations); |
|
| 20 |
+ |
|
| 19 | 21 |
const jobsOperationsWithKoefs = map(jobsOperation => {
|
| 20 | 22 |
const koef = reduce( |
| 21 | 23 |
(acc, i) => acc + Math.abs(processorsCount - 2 * i + 1) * jobsOperation.operations[i-1].t, |
| ... | ... |
@@ -1,15 +1,14 @@ |
| 1 | 1 |
import {
|
| 2 |
- addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
| 3 |
- filter, reverse |
|
| 2 |
+ findIndex, map, propEq, reduce, filter, reverse |
|
| 4 | 3 |
} from 'rambda'; |
| 5 | 4 |
import type { JobId } from '../../types';
|
| 6 |
-import { remove, slice } from 'ramda';
|
|
| 5 |
+import { max, remove } from 'ramda';
|
|
| 6 |
+import { fillCorrupted } from '../moore';
|
|
| 7 | 7 |
|
| 8 | 8 |
type JobTime = {
|
| 9 | 9 |
jobId: JobId, |
| 10 | 10 |
t: number, |
| 11 | 11 |
d: number, |
| 12 |
- c: number |
|
| 13 | 12 |
}; |
| 14 | 13 |
|
| 15 | 14 |
const findMaxTJob = (jobTimes: JobTime[]) => |
| ... | ... |
@@ -32,6 +31,8 @@ const updateCTime = (jobTimes: JobTime[]) => {
|
| 32 | 32 |
}; |
| 33 | 33 |
|
| 34 | 34 |
export const createSchedule = (jobs: JobTime[]) => {
|
| 35 |
+ jobs = fillCorrupted(jobs); |
|
| 36 |
+ |
|
| 35 | 37 |
let fi = reduce((sum, job: JobTime) => sum + job.t, 0, jobs); |
| 36 | 38 |
let jobsD = jobs; |
| 37 | 39 |
let R = []; |
| ... | ... |
@@ -58,10 +59,13 @@ export const createSchedule = (jobs: JobTime[]) => {
|
| 58 | 58 |
export default (jobs: JobTime[]) => {
|
| 59 | 59 |
const schedule = createSchedule(jobs); |
| 60 | 60 |
|
| 61 |
+ if (!schedule) return null; |
|
| 62 |
+ |
|
| 61 | 63 |
const normalizedSchedule = map((jobTime: JobTime) => ({
|
| 62 | 64 |
processor: 1, |
| 63 | 65 |
startTime: jobTime.c - jobTime.t, |
| 64 | 66 |
endTime: jobTime.c, |
| 67 |
+ delayed: max(0, jobTime.c - jobTime.d) |
|
| 65 | 68 |
}), schedule); |
| 66 | 69 |
|
| 67 | 70 |
return [normalizedSchedule]; |
| ... | ... |
@@ -50,15 +50,15 @@ test('create gantt', () => {
|
| 50 | 50 |
|
| 51 | 51 |
expect(template.length).toBe(1); |
| 52 | 52 |
expect(template[0].length).toBe(11); |
| 53 |
- expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 1 });
|
|
| 54 |
- expect(template[0][1]).toEqual({ processor: 1, startTime: 1, endTime: 5 });
|
|
| 55 |
- expect(template[0][2]).toEqual({ processor: 1, startTime: 5, endTime: 9 });
|
|
| 56 |
- expect(template[0][3]).toEqual({ processor: 1, startTime: 9, endTime: 15 });
|
|
| 57 |
- expect(template[0][4]).toEqual({ processor: 1, startTime: 15, endTime: 21 });
|
|
| 58 |
- expect(template[0][5]).toEqual({ processor: 1, startTime: 21, endTime: 28 });
|
|
| 59 |
- expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 30 });
|
|
| 60 |
- expect(template[0][7]).toEqual({ processor: 1, startTime: 30, endTime: 33 });
|
|
| 61 |
- expect(template[0][8]).toEqual({ processor: 1, startTime: 33, endTime: 40 });
|
|
| 62 |
- expect(template[0][9]).toEqual({ processor: 1, startTime: 40, endTime: 46 });
|
|
| 63 |
- expect(template[0][10]).toEqual({ processor: 1, startTime: 46, endTime: 50 });
|
|
| 53 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 1, delayed: 0 });
|
|
| 54 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 1, endTime: 5, delayed: 0 });
|
|
| 55 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 5, endTime: 9, delayed: 0 });
|
|
| 56 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 9, endTime: 15, delayed: 0 });
|
|
| 57 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 15, endTime: 21, delayed: 0 });
|
|
| 58 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 21, endTime: 28, delayed: 0 });
|
|
| 59 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 30, delayed: 0 });
|
|
| 60 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 30, endTime: 33, delayed: 0 });
|
|
| 61 |
+ expect(template[0][8]).toEqual({ processor: 1, startTime: 33, endTime: 40, delayed: 0 });
|
|
| 62 |
+ expect(template[0][9]).toEqual({ processor: 1, startTime: 40, endTime: 46, delayed: 0 });
|
|
| 63 |
+ expect(template[0][10]).toEqual({ processor: 1, startTime: 46, endTime: 50, delayed: 0 });
|
|
| 64 | 64 |
}); |
| 65 | 65 |
\ No newline at end of file |
| ... | ... |
@@ -1,30 +1,17 @@ |
| 1 | 1 |
import {
|
| 2 |
- addIndex, |
|
| 3 |
- findIndex, |
|
| 4 | 2 |
find, |
| 5 | 3 |
map, |
| 6 |
- propEq, |
|
| 7 | 4 |
reduce, |
| 8 | 5 |
sort, |
| 9 |
- range, |
|
| 10 |
- concat, |
|
| 11 | 6 |
filter, |
| 12 |
- reverse, |
|
| 13 |
- prop, |
|
| 14 | 7 |
flatten, |
| 15 |
- uniq, |
|
| 16 | 8 |
reject, |
| 17 | 9 |
contains, |
| 18 | 10 |
omit, |
| 19 |
- equals, |
|
| 20 |
- update, |
|
| 21 |
- times, |
|
| 22 |
- add, |
|
| 23 |
- pluck, |
|
| 24 |
- last, forEach, all |
|
| 11 |
+ forEach, all |
|
| 25 | 12 |
} from 'rambda'; |
| 26 | 13 |
import type { JobId } from '../../types';
|
| 27 |
-import { groupBy, max, min, remove, slice } from 'ramda';
|
|
| 14 |
+import { groupBy } from 'ramda';
|
|
| 28 | 15 |
import { updateCTime } from '../mcnaught';
|
| 29 | 16 |
|
| 30 | 17 |
type JobTime = {
|
| ... | ... |
@@ -41,7 +28,13 @@ const noSuccJobs = (jobTimes: JobTime[]) => {
|
| 41 | 41 |
const successors = (id: JobId, jobTimes: JobTime[]) => |
| 42 | 42 |
filter(({ anc }) => contains(id, anc), jobTimes);
|
| 43 | 43 |
|
| 44 |
+const fillCorrupted = (jobs: JobTime[]) => map( |
|
| 45 |
+ ({ anc, ...rest }) => ({ ...rest, anc: anc || [] }),
|
|
| 46 |
+ jobs); |
|
| 47 |
+ |
|
| 44 | 48 |
export const createSchedule = (jobTimes: JobTime[], m: number) => {
|
| 49 |
+ jobTimes = fillCorrupted(jobTimes); |
|
| 50 |
+ |
|
| 45 | 51 |
let T = reduce((acc, jobTime) => acc + jobTime.t, 0, jobTimes); |
| 46 | 52 |
|
| 47 | 53 |
let noSucc = noSuccJobs(jobTimes); |
| ... | ... |
@@ -124,6 +117,8 @@ export const createSchedule = (jobTimes: JobTime[], m: number) => {
|
| 124 | 124 |
export default (jobTimes: JobTime[], processorsCount: number) => {
|
| 125 | 125 |
const schedule = createSchedule(jobTimes, processorsCount); |
| 126 | 126 |
|
| 127 |
+ if (!schedule) return null; |
|
| 128 |
+ |
|
| 127 | 129 |
const grouped = Object.values(groupBy(jobTime => jobTime.processor, schedule)); |
| 128 | 130 |
|
| 129 | 131 |
return map(map(job => ({
|
| ... | ... |
@@ -187,6 +187,7 @@ class Box extends React.PureComponent {
|
| 187 | 187 |
as = 'div', |
| 188 | 188 |
className, |
| 189 | 189 |
style, |
| 190 |
+ nativeRef, |
|
| 190 | 191 |
...props |
| 191 | 192 |
} = this.props; |
| 192 | 193 |
|
| ... | ... |
@@ -204,7 +205,7 @@ class Box extends React.PureComponent {
|
| 204 | 204 |
className |
| 205 | 205 |
}; |
| 206 | 206 |
|
| 207 |
- return React.createElement(as, { ...restProps, ...styles });
|
|
| 207 |
+ return React.createElement(as, { ...restProps, ...styles, ref: nativeRef });
|
|
| 208 | 208 |
} |
| 209 | 209 |
} |
| 210 | 210 |
|
| ... | ... |
@@ -12,116 +12,130 @@ import PropTypes from 'proptypes'; |
| 12 | 12 |
import Select from 'react-select'; |
| 13 | 13 |
import 'react-select/dist/react-select.css'; |
| 14 | 14 |
import {
|
| 15 |
- filter, find, findIndex, flatten, forEach, identity, map, prop, propEq, reject, uniq, update |
|
| 15 |
+ filter, find, flatten, identity, map, prop, propEq, reject, uniq, update |
|
| 16 | 16 |
} from 'rambda'; |
| 17 | 17 |
import { insert, remove } from 'ramda';
|
| 18 | 18 |
import theme from '../theme'; |
| 19 | 19 |
|
| 20 |
-const allJobs = {
|
|
| 21 |
- whatever: 'Vševediaci', |
|
| 22 |
- |
|
| 23 |
- marketResearch: 'Prieskum trhu', |
|
| 24 |
- |
|
| 25 |
- designUi: 'Dizajnér – UI', |
|
| 26 |
- designUx: 'Dizajnér – UX', |
|
| 27 |
- |
|
| 28 |
- architect: 'Softwarový architekt', |
|
| 29 |
- |
|
| 30 |
- devFrontend: 'Developer – frontend', |
|
| 31 |
- devBackend: 'Developer – backend', |
|
| 32 |
- devDb: 'Developer – databázy', |
|
| 33 |
- |
|
| 34 |
- admin: 'Serverový administrátor', |
|
| 35 |
- |
|
| 36 |
- tester: 'Tester', |
|
| 37 |
- |
|
| 38 |
- analyst: 'Analytik', |
|
| 39 |
- |
|
| 40 |
- qa: 'Quality assurance', |
|
| 41 |
- |
|
| 42 |
- deployManager: 'Deploy' |
|
| 43 |
-}; |
|
| 44 |
- |
|
| 45 | 20 |
const nextWorkerName = (rows) => |
| 46 | 21 |
`${rows.length + 1}. pracovník`;
|
| 47 | 22 |
|
| 48 | 23 |
class Form extends React.PureComponent {
|
| 49 | 24 |
state = {
|
| 25 |
+ allJobs: [], |
|
| 50 | 26 |
rows: [ |
| 51 | 27 |
// {
|
| 52 | 28 |
// id: uuid(), |
| 53 | 29 |
// name: 'pracovnik 1', |
| 54 |
- // preempt: false, |
|
| 55 |
- // linkedTo: null, |
|
| 56 | 30 |
// jobs: [ |
| 57 |
- // { id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 },
|
|
| 58 |
- // { id: uuid(), name: 'backend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 }
|
|
| 31 |
+ // { id: uuid(), name: 'frontend', t: '', d: '', w: '', anc: [] },
|
|
| 32 |
+ // { id: uuid(), name: 'backend', t: '', d: '', w: '', anc: [] }
|
|
| 59 | 33 |
// ] |
| 60 | 34 |
// }, |
| 61 | 35 |
// {
|
| 62 | 36 |
// id: uuid(), |
| 63 | 37 |
// name: 'pracovnik 2', |
| 64 |
- // preempt: false, |
|
| 65 |
- // linkedTo: null, |
|
| 66 | 38 |
// jobs: [ |
| 67 |
- // { id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 },
|
|
| 68 |
- // { id: uuid(), name: 'backend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 }
|
|
| 39 |
+ // { id: uuid(), name: 'frontend', t: '', d: '', w: '', anc: [] },
|
|
| 40 |
+ // { id: uuid(), name: 'backend', t: '', d: '', w: '', anc: [] }
|
|
| 69 | 41 |
// ] |
| 70 | 42 |
// } |
| 71 |
- ] |
|
| 43 |
+ ], |
|
| 44 |
+ preempt: false, |
|
| 72 | 45 |
}; |
| 73 | 46 |
|
| 74 | 47 |
onSubmit = (values) => {
|
| 75 |
- console.log(values); |
|
| 48 |
+ if (!values || !values.rows.length) return; |
|
| 49 |
+ |
|
| 50 |
+ const { onSubmit } = this.props;
|
|
| 51 |
+ onSubmit(values); |
|
| 76 | 52 |
}; |
| 77 | 53 |
|
| 78 |
- addWorker = (setFieldValue, rows) => {
|
|
| 54 |
+ addWorker = (setFieldValue, rows, allJobs) => {
|
|
| 79 | 55 |
const jobs = [{
|
| 80 |
- id: uuid(), name: 'whatever', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 |
|
| 56 |
+ id: uuid(), name: allJobs[0], t: '', d: '', w: '', anc: [] |
|
| 81 | 57 |
}]; |
| 82 | 58 |
|
| 83 | 59 |
const updatedRows = [...rows, { id: uuid(), name: nextWorkerName(rows), jobs } ];
|
| 84 | 60 |
setFieldValue('rows', updatedRows);
|
| 85 | 61 |
}; |
| 86 | 62 |
|
| 63 |
+ addTemplateWorker = (setFieldValue, allJobs) => (e) => {
|
|
| 64 |
+ if (e.keyCode !== undefined && e.keyCode !== 13) return; |
|
| 65 |
+ |
|
| 66 |
+ e.preventDefault(); |
|
| 67 |
+ |
|
| 68 |
+ const val = this.new.value.replace(/:/g, ''); |
|
| 69 |
+ const updatedJobs = [...allJobs, val ]; |
|
| 70 |
+ setFieldValue('allJobs', updatedJobs);
|
|
| 71 |
+ |
|
| 72 |
+ this.new.value = null; |
|
| 73 |
+ }; |
|
| 74 |
+ |
|
| 87 | 75 |
render() {
|
| 88 | 76 |
return ( |
| 89 |
- <Formik initialValues={this.state} onSubmit={this.onSubmit} render={({ setFieldValue, values: { rows } }) => (
|
|
| 77 |
+ <Formik initialValues={this.state} onSubmit={this.onSubmit} render={({ setFieldValue, values: { rows, allJobs } }) => (
|
|
| 90 | 78 |
<FormikForm> |
| 91 |
- <h1>Konfigurácia projektu</h1> |
|
| 92 |
- |
|
| 93 |
- {rows.length ?
|
|
| 94 |
- <Box flexDirection="row" marginBottom={10}>
|
|
| 95 |
- <Box flexBasis="15%" flexShrink={0}><strong>Pracovník</strong></Box>
|
|
| 96 |
- <Box flexBasis="63%" flexShrink={0}><strong>Úlohy</strong></Box>
|
|
| 97 |
- <Box flexBasis="12%" flexShrink={0} align="center"><strong>Prerušenie</strong></Box>
|
|
| 98 |
- </Box> : null |
|
| 99 |
- } |
|
| 100 |
- |
|
| 101 |
- <FieldArray name="rows" render={arrayHelpers => (
|
|
| 102 |
- <React.Fragment> |
|
| 103 |
- {rows.map((row, i) => <Row key={i} {...row} arrayHelpers={arrayHelpers} prefix={`rows.${i}`} index={i} />)}
|
|
| 104 |
- </React.Fragment> |
|
| 105 |
- )} /> |
|
| 106 |
- |
|
| 107 |
- <Box flexDirection="row" marginBottom={10}>
|
|
| 108 |
- <Box |
|
| 109 |
- flexShrink={0}
|
|
| 110 |
- onClick={() => this.addWorker(setFieldValue, rows)}
|
|
| 111 |
- {...theme.buttons.primary}
|
|
| 112 |
- > |
|
| 113 |
- + Nový pracovník |
|
| 114 |
- </Box> |
|
| 79 |
+ <h1>1/3 Vytvorenie pracovníkov</h1> |
|
| 80 |
+ <Box flexWrap="wrap" flexDirection="row"> |
|
| 81 |
+ {allJobs.map((_, key) => (
|
|
| 82 |
+ <Box key={key} width={300} flexDirection="row" marginBottom={10}>
|
|
| 83 |
+ <Box as={Field} name={`allJobs.${key}`} flex={1} marginRight={5} onChange={({ target: { value } }) => setFieldValue(`allJobs.${key}`, value)} />
|
|
| 84 |
+ <Box cursor="pointer" onClick={() => setFieldValue(`allJobs`, remove(key, 1, allJobs))} color="red" marginRight={20}>✕</Box>
|
|
| 85 |
+ </Box> |
|
| 86 |
+ ))} |
|
| 115 | 87 |
</Box> |
| 116 | 88 |
|
| 117 |
- <Box as="button" margin="auto" width="20rem" alignItems="center" padding={5}
|
|
| 118 |
- {...theme.buttons.primary} backgroundColor="#C89C93">
|
|
| 119 |
- <Box fontSize="1rem">Cieľ:</Box> |
|
| 120 |
- <Box as="select" marginTop={5}>
|
|
| 121 |
- <option>Minimalizovať počet oneskorených úloh</option> |
|
| 122 |
- <option>2...</option> |
|
| 123 |
- </Box> |
|
| 89 |
+ <Box width={300} flexDirection="row" marginBottom={10}>
|
|
| 90 |
+ <Box as="input" placeholder="Nový pracovník" onKeyDown={this.addTemplateWorker(setFieldValue, allJobs)} nativeRef={c => this.new = c} marginRight={5} flex={1} />
|
|
| 91 |
+ <Box cursor="pointer" onClick={this.addTemplateWorker(setFieldValue, allJobs)} color="green" fontSize="1rem">+</Box>
|
|
| 124 | 92 |
</Box> |
| 93 |
+ |
|
| 94 |
+ {allJobs.length ? (
|
|
| 95 |
+ <div> |
|
| 96 |
+ <h1>2/3 Konfigurácia projektu</h1> |
|
| 97 |
+ |
|
| 98 |
+ {rows.length ?
|
|
| 99 |
+ <Box flexDirection="row" marginBottom={10}>
|
|
| 100 |
+ <Box flexBasis="20%" flexShrink={0}><strong>Pracovníci</strong></Box>
|
|
| 101 |
+ <Box flexBasis="63%" flexShrink={0}><strong>Úlohy</strong></Box>
|
|
| 102 |
+ </Box> : null |
|
| 103 |
+ } |
|
| 104 |
+ |
|
| 105 |
+ <FieldArray name="rows" render={arrayHelpers => (
|
|
| 106 |
+ <React.Fragment> |
|
| 107 |
+ {rows.map((row, i) => <Row key={i} {...row} arrayHelpers={arrayHelpers}
|
|
| 108 |
+ prefix={`rows.${i}`} index={i}/>)}
|
|
| 109 |
+ </React.Fragment> |
|
| 110 |
+ )}/> |
|
| 111 |
+ |
|
| 112 |
+ <Box flexDirection="row" marginBottom={10}>
|
|
| 113 |
+ <Box |
|
| 114 |
+ flexShrink={0}
|
|
| 115 |
+ onClick={() => this.addWorker(setFieldValue, rows, allJobs)}
|
|
| 116 |
+ {...theme.buttons.primary}
|
|
| 117 |
+ > |
|
| 118 |
+ + Nový pracovník |
|
| 119 |
+ </Box> |
|
| 120 |
+ </Box> |
|
| 121 |
+ |
|
| 122 |
+ {rows.length ?
|
|
| 123 |
+ <div> |
|
| 124 |
+ <Box as="label" justifyContent="center" flexDirection="row"> |
|
| 125 |
+ <Field type="checkbox" name="preempt"/> |
|
| 126 |
+ <Box as="em" color="gray" textAlign="center" fontSize="0.9em"> |
|
| 127 |
+ Úlohy pracovníkov možno prerušiť |
|
| 128 |
+ </Box> |
|
| 129 |
+ </Box> |
|
| 130 |
+ |
|
| 131 |
+ <Box as="button" margin="auto" marginTop={10} width="20rem" alignItems="center"
|
|
| 132 |
+ padding={5} {...theme.buttons.primary} backgroundColor="#C89C93" fontSize="1rem">
|
|
| 133 |
+ Vytvoriť rozvrh |
|
| 134 |
+ </Box> |
|
| 135 |
+ </div> |
|
| 136 |
+ : null} |
|
| 137 |
+ </div> |
|
| 138 |
+ ) : null} |
|
| 125 | 139 |
</FormikForm> |
| 126 | 140 |
)}> |
| 127 | 141 |
</Formik> |
| ... | ... |
@@ -159,8 +173,8 @@ const SortableJob = SortableElement( |
| 159 | 159 |
}; |
| 160 | 160 |
|
| 161 | 161 |
render() {
|
| 162 |
- const { myIndex, rowIndex, prefix, job, existingJobs: _existingJobs } = this.props;
|
|
| 163 |
- const { formik: { values: { rows } } } = this.context;
|
|
| 162 |
+ const { prefix, job, existingJobs: _existingJobs } = this.props;
|
|
| 163 |
+ const { formik: { values: { rows, allJobs } } } = this.context;
|
|
| 164 | 164 |
|
| 165 | 165 |
const existingJobs = map( |
| 166 | 166 |
value => {
|
| ... | ... |
@@ -168,7 +182,7 @@ const SortableJob = SortableElement( |
| 168 | 168 |
const row = find(propEq('id', idRow), rows);
|
| 169 | 169 |
const jobName = find(propEq('id', idJob), row.jobs).name;
|
| 170 | 170 |
|
| 171 |
- return ({ value, label: `${row.name} – ${allJobs[jobName]}` });
|
|
| 171 |
+ return ({ value, label: `${row.name} – ${jobName}` });
|
|
| 172 | 172 |
}, |
| 173 | 173 |
reject( |
| 174 | 174 |
value => {
|
| ... | ... |
@@ -187,55 +201,50 @@ const SortableJob = SortableElement( |
| 187 | 187 |
className="sortable-inactive" |
| 188 | 188 |
> |
| 189 | 189 |
<Box marginRight={10} fontSize="1.5rem" width={20}>
|
| 190 |
- {!rows[rowIndex].linkedTo &&
|
|
| 191 | 190 |
<DragHandle/> |
| 192 |
- } |
|
| 193 | 191 |
</Box> |
| 194 | 192 |
|
| 195 | 193 |
<Box flex={1} padding={1}>
|
| 196 | 194 |
<Box width={200} marginBottom={10} flexDirection="row">
|
| 197 |
- <Field component="select" name={`${prefix}.name`} disabled={rows[rowIndex].linkedTo}>
|
|
| 198 |
- {Object.keys(allJobs).map(key => (
|
|
| 199 |
- <option value={key} key={key}>{allJobs[key]}</option>
|
|
| 195 |
+ <Field component="select" name={`${prefix}.name`}>
|
|
| 196 |
+ {allJobs.map((job, key) => (
|
|
| 197 |
+ <option value={job} key={key}>{job}</option>
|
|
| 200 | 198 |
))} |
| 201 | 199 |
</Field> |
| 202 |
- {!rows[rowIndex].linkedTo &&
|
|
| 203 | 200 |
<Box cursor="pointer" onClick={this.deleteJob} marginLeft={5} color="red">✕</Box>
|
| 204 |
- } |
|
| 205 | 201 |
</Box> |
| 206 | 202 |
|
| 207 | 203 |
<Box flexDirection="row" justifyContent="space-between" marginBottom={10}>
|
| 208 | 204 |
<Box flexBasis="30%"> |
| 209 | 205 |
<label> |
| 210 | 206 |
Trvanie: <br /> |
| 211 |
- <Field type="number" step={1} name={`${prefix}.t`} style={{ width: '100%' }} />
|
|
| 207 |
+ <Field type="number" min={0} step={1} name={`${prefix}.t`} style={{ width: '100%' }} required />
|
|
| 212 | 208 |
</label> |
| 213 | 209 |
</Box> |
| 214 | 210 |
<Box flexBasis="30%"> |
| 215 | 211 |
<label> |
| 216 | 212 |
Deadline: <br /> |
| 217 |
- <Field type="number" step={1} name={`${prefix}.d`} style={{ width: '100%' }} />
|
|
| 213 |
+ <Field type="number" min={0} step={1} name={`${prefix}.d`} style={{ width: '100%' }} disabled={rows.length > 1} />
|
|
| 218 | 214 |
</label> |
| 219 | 215 |
</Box> |
| 220 | 216 |
<Box flexBasis="30%"> |
| 221 | 217 |
<label> |
| 222 |
- Váha: <br /> |
|
| 223 |
- <Field type="number" min={0} max={1} step={0.1} name={`${prefix}.w`} style={{ width: '100%' }} />
|
|
| 218 |
+ Váha: (1-10) <br /> |
|
| 219 |
+ <Field type="number" min={1} max={10} step={1} name={`${prefix}.w`} style={{ width: '100%' }} />
|
|
| 224 | 220 |
</label> |
| 225 | 221 |
</Box> |
| 226 | 222 |
</Box> |
| 227 | 223 |
|
| 228 | 224 |
<Box> |
| 229 |
- <Box as="label" flexDirection="row" marginBottom={5}>
|
|
| 230 |
- <Field type="radio" name={`${prefix}.succOrAnc`} value={0} checked={job.succOrAnc == '0'} disabled={rows[rowIndex].linkedTo}/> Bez nadväznosti
|
|
| 231 |
- </Box> |
|
| 225 |
+ {/*<Box as="label" flexDirection="row" marginBottom={5}>*/}
|
|
| 226 |
+ {/*<Field type="radio" name={`${prefix}.succOrAnc`} value={0} checked={job.succOrAnc == '0'} disabled={rows[rowIndex].linkedTo}/> Bez nadväznosti*/}
|
|
| 227 |
+ {/*</Box>*/}
|
|
| 232 | 228 |
<Box marginBottom={5}>
|
| 233 | 229 |
<Box as="label" flexDirection="row"> |
| 234 |
- <Field type="radio" name={`${prefix}.succOrAnc`} value={1} checked={job.succOrAnc == '1'} disabled={rows[rowIndex].linkedTo} /> Predchodcovia
|
|
| 230 |
+ Predchodcovia |
|
| 235 | 231 |
</Box> |
| 236 |
- <Box flex={1} marginLeft={10} marginTop={5}>
|
|
| 232 |
+ <Box flex={1} marginTop={5}>
|
|
| 237 | 233 |
<Select |
| 238 |
- disabled={job.succOrAnc != '1' || rows[rowIndex].linkedTo}
|
|
| 239 | 234 |
multi |
| 240 | 235 |
onChange={e => this.handleSelectChange('anc', e)}
|
| 241 | 236 |
options={existingJobs}
|
| ... | ... |
@@ -244,21 +253,6 @@ const SortableJob = SortableElement( |
| 244 | 244 |
/> |
| 245 | 245 |
</Box> |
| 246 | 246 |
</Box> |
| 247 |
- <Box> |
|
| 248 |
- <Box as="label" flexDirection="row"> |
|
| 249 |
- <Field type="radio" name={`${prefix}.succOrAnc`} value={2} checked={job.succOrAnc == '2'} disabled={rows[rowIndex].linkedTo} /> Nasledovníci
|
|
| 250 |
- </Box> |
|
| 251 |
- <Box flex={1} marginLeft={10} marginTop={5}>
|
|
| 252 |
- <Select |
|
| 253 |
- disabled={job.succOrAnc != '2' || rows[rowIndex].linkedTo}
|
|
| 254 |
- multi |
|
| 255 |
- onChange={e => this.handleSelectChange('succ', e)}
|
|
| 256 |
- options={existingJobs}
|
|
| 257 |
- removeSelected |
|
| 258 |
- value={job.succ}
|
|
| 259 |
- /> |
|
| 260 |
- </Box> |
|
| 261 |
- </Box> |
|
| 262 | 247 |
</Box> |
| 263 | 248 |
</Box> |
| 264 | 249 |
</Box> |
| ... | ... |
@@ -322,9 +316,9 @@ class Row extends React.PureComponent {
|
| 322 | 322 |
|
| 323 | 323 |
addJob = () => {
|
| 324 | 324 |
const { index } = this.props;
|
| 325 |
- const { formik: { setFieldValue, values: { rows } } } = this.context;
|
|
| 325 |
+ const { formik: { setFieldValue, values: { rows, allJobs } } } = this.context;
|
|
| 326 | 326 |
const newJob = {
|
| 327 |
- id: uuid(), name: 'whatever', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 |
|
| 327 |
+ id: uuid(), name: allJobs[0], t: '', d: '', w: '', anc: [] |
|
| 328 | 328 |
}; |
| 329 | 329 |
|
| 330 | 330 |
const jobs = [...rows[index].jobs, newJob]; |
| ... | ... |
@@ -333,27 +327,20 @@ class Row extends React.PureComponent {
|
| 333 | 333 |
}; |
| 334 | 334 |
|
| 335 | 335 |
deleteRow = () => {
|
| 336 |
- const { index, id } = this.props;
|
|
| 336 |
+ const { index } = this.props;
|
|
| 337 | 337 |
const { formik: { setFieldValue, values: { rows } } } = this.context;
|
| 338 | 338 |
|
| 339 | 339 |
let newRows = remove(index, 1, rows); |
| 340 | 340 |
|
| 341 |
- const alsoDelete = filter(propEq('linkedTo', id), rows);
|
|
| 342 |
- |
|
| 343 |
- forEach(row => {
|
|
| 344 |
- const index = findIndex(propEq('id', row.id), newRows);
|
|
| 345 |
- newRows = remove(index, 1, newRows); |
|
| 346 |
- }, alsoDelete); |
|
| 347 |
- |
|
| 348 | 341 |
setFieldValue('rows', newRows);
|
| 349 | 342 |
}; |
| 350 | 343 |
|
| 351 | 344 |
duplicate = () => {
|
| 352 |
- const { id, index, name, preempt, jobs } = this.props;
|
|
| 345 |
+ const { index, name, preempt, jobs } = this.props;
|
|
| 353 | 346 |
const { formik: { setFieldValue, values: { rows } } } = this.context;
|
| 354 | 347 |
|
| 355 | 348 |
const jobsCopy = map(job => ({ ...job }), jobs);
|
| 356 |
- const newRow = { id: uuid(), name: `${name} – kópia`, preempt, linkedTo: id, jobs: jobsCopy };
|
|
| 349 |
+ const newRow = { id: uuid(), name: `${name} – kópia`, preempt, jobs: jobsCopy };
|
|
| 357 | 350 |
|
| 358 | 351 |
const updatedRows = insert(index + 1, newRow, rows); |
| 359 | 352 |
|
| ... | ... |
@@ -361,16 +348,16 @@ class Row extends React.PureComponent {
|
| 361 | 361 |
}; |
| 362 | 362 |
|
| 363 | 363 |
render() {
|
| 364 |
- const { index, prefix, name, preempt, linkedTo, jobs } = this.props;
|
|
| 364 |
+ const { index, prefix, name, jobs } = this.props;
|
|
| 365 | 365 |
const { draggingIndex } = this.state;
|
| 366 | 366 |
|
| 367 | 367 |
const existingJobs = this.findExistingJobs(); |
| 368 | 368 |
|
| 369 | 369 |
return ( |
| 370 | 370 |
<Box flexDirection="row" marginBottom={30} padding={10} className="row">
|
| 371 |
- <Box flexBasis="15%" flexShrink={0} paddingRight={10}>
|
|
| 371 |
+ <Box flexBasis="20%" flexShrink={0} paddingRight={10} flexDirection="row">
|
|
| 372 | 372 |
<strong>{name}</strong>
|
| 373 |
- <Box cursor="pointer" onClick={this.deleteRow} color="red">✕</Box>
|
|
| 373 |
+ <Box cursor="pointer" onClick={this.deleteRow} color="red" marginLeft={5}>✕</Box>
|
|
| 374 | 374 |
</Box> |
| 375 | 375 |
<Box flexBasis="63%" flexShrink={0}>
|
| 376 | 376 |
<FieldArray render={() => (
|
| ... | ... |
@@ -386,22 +373,11 @@ class Row extends React.PureComponent {
|
| 386 | 386 |
draggingIndex={draggingIndex}
|
| 387 | 387 |
/> |
| 388 | 388 |
)} /> |
| 389 |
- {!linkedTo &&
|
|
| 390 | 389 |
<Box onClick={this.addJob} {...theme.buttons.secondary} alignSelf="flex-start">
|
| 391 | 390 |
+ Nová úloha |
| 392 | 391 |
</Box> |
| 393 |
- } |
|
| 394 |
- </Box> |
|
| 395 |
- <Box flexBasis="12%" flexShrink={0}>
|
|
| 396 |
- <Box as="label" alignItems="center" paddingTop={10}>
|
|
| 397 |
- <Field type="checkbox" name={`${prefix}.preempt`} disabled={linkedTo} />
|
|
| 398 |
- <Box as="em" color="gray" textAlign="center" padding={10} fontSize="0.9em">
|
|
| 399 |
- Úlohy pracovníka možno prerušiť |
|
| 400 |
- </Box> |
|
| 401 |
- </Box> |
|
| 402 | 392 |
</Box> |
| 403 |
- <Box alignItems="flex-end"> |
|
| 404 |
- {!linkedTo &&
|
|
| 393 |
+ <Box alignItems="center" flex={1}>
|
|
| 405 | 394 |
<Box |
| 406 | 395 |
onClick={this.duplicate}
|
| 407 | 396 |
{...theme.buttons.tertiary}
|
| ... | ... |
@@ -413,7 +389,6 @@ class Row extends React.PureComponent {
|
| 413 | 413 |
> |
| 414 | 414 |
<span style={{ fontSize: '1.8em', paddingRight: '0.2em' }}>⩇</span> Duplikovať
|
| 415 | 415 |
</Box> |
| 416 |
- } |
|
| 417 | 416 |
</Box> |
| 418 | 417 |
</Box> |
| 419 | 418 |
) |
| ... | ... |
@@ -61,7 +61,7 @@ class Gannt extends React.PureComponent {
|
| 61 | 61 |
|
| 62 | 62 |
return ( |
| 63 | 63 |
<div ref={measureRef}>
|
| 64 |
- <h1>Ganttov diagram</h1> |
|
| 64 |
+ <h1>3/3 Ganttov diagram</h1> |
|
| 65 | 65 |
|
| 66 | 66 |
<ScatterChart width={width} height={height} data={data[0]} margin={theme.gantt.margin}>
|
| 67 | 67 |
<CartesianGrid |
| ... | ... |
@@ -48,13 +48,17 @@ export const normalizeData = data => |
| 48 | 48 |
|
| 49 | 49 |
forEach(startTime => {
|
| 50 | 50 |
if (!dataObj[startTime]) {
|
| 51 |
- dataObj[startTime] = { startTime };
|
|
| 51 |
+ dataObj[startTime] = { startTime, processor: last(processorData).processor };
|
|
| 52 | 52 |
} |
| 53 | 53 |
}, rangeTime); |
| 54 | 54 |
|
| 55 | 55 |
const filledData = Object.values(dataObj); |
| 56 | 56 |
|
| 57 |
- filledData.push({ startTime: last(processorData).endTime });
|
|
| 57 |
+ filledData.push({
|
|
| 58 |
+ startTime: last(processorData).endTime, |
|
| 59 |
+ endTime: last(processorData).endTime + step, |
|
| 60 |
+ processor: last(processorData).processor |
|
| 61 |
+ }); |
|
| 58 | 62 |
|
| 59 | 63 |
return filledData; |
| 60 | 64 |
}, data); |
| 61 | 65 |
\ No newline at end of file |
| 62 | 66 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,71 @@ |
| 0 |
+import { find, indexOf, last, map, prop, reduce, sort } from 'rambda';
|
|
| 1 |
+import { max } from 'ramda';
|
|
| 2 |
+ |
|
| 3 |
+export const selectAlgorithms = (rows, preempt) => {
|
|
| 4 |
+ const processorsCount = rows.length; |
|
| 5 |
+ |
|
| 6 |
+ if (!processorsCount) return null; |
|
| 7 |
+ |
|
| 8 |
+ if (processorsCount === 1) {
|
|
| 9 |
+ const existD = !!find(prop('d'), rows[0].jobs);
|
|
| 10 |
+ const existAnc = !!find(({ anc }) => Array.isArray(anc) && anc.length, rows[0].jobs);
|
|
| 11 |
+ |
|
| 12 |
+ if (existAnc) {
|
|
| 13 |
+ return ['lawler']; |
|
| 14 |
+ } |
|
| 15 |
+ |
|
| 16 |
+ if (existD) {
|
|
| 17 |
+ return ['moore', 'smith']; |
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ return ['moore']; |
|
| 21 |
+ } |
|
| 22 |
+ |
|
| 23 |
+ const existAnc = find( |
|
| 24 |
+ ({ jobs }) => !!find(({ anc }) => Array.isArray(anc) && anc.length, jobs),
|
|
| 25 |
+ rows); |
|
| 26 |
+ |
|
| 27 |
+ if (existAnc) {
|
|
| 28 |
+ return ['vahy']; |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ let multi = ['campbel', 'grupt', 'palmer']; |
|
| 32 |
+ |
|
| 33 |
+ if (processorsCount === 2) {
|
|
| 34 |
+ multi.push('johnson');
|
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ if (preempt) {
|
|
| 38 |
+ multi.push('mcnaught');
|
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ return multi; |
|
| 42 |
+}; |
|
| 43 |
+ |
|
| 44 |
+export const najneomeskanejsiResult = (algoResults: []) => {
|
|
| 45 |
+ if (!algoResults.length) return -1; |
|
| 46 |
+ |
|
| 47 |
+ const delaysSum = reduce((acc, processorJobs) => {
|
|
| 48 |
+ const delays = reduce((acc, { delayed }) => acc + delayed, 0, processorJobs);
|
|
| 49 |
+ return acc + delays; |
|
| 50 |
+ }, 0); |
|
| 51 |
+ |
|
| 52 |
+ const delaysTimes = map(delaysSum, algoResults); |
|
| 53 |
+ const sortedDelaysTimes = sort((a, b) => a - b, delaysTimes); |
|
| 54 |
+ |
|
| 55 |
+ return indexOf(sortedDelaysTimes[0], delaysTimes); |
|
| 56 |
+}; |
|
| 57 |
+ |
|
| 58 |
+export const fastestResult = (algoResults: []) => {
|
|
| 59 |
+ if (!algoResults.length) return -1; |
|
| 60 |
+ |
|
| 61 |
+ const slowestProcessorEndTime = reduce((endTime, processorJobs) => {
|
|
| 62 |
+ const lastEndTime = last(processorJobs).endTime; |
|
| 63 |
+ return max(lastEndTime, endTime); |
|
| 64 |
+ }, 0); |
|
| 65 |
+ |
|
| 66 |
+ const endTimes = map(slowestProcessorEndTime, algoResults); |
|
| 67 |
+ const sortedEndTimes = sort((a, b) => a - b, endTimes); |
|
| 68 |
+ |
|
| 69 |
+ return indexOf(sortedEndTimes[0], endTimes); |
|
| 70 |
+}; |
|
| 0 | 71 |
\ No newline at end of file |
| 1 | 72 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,52 @@ |
| 0 |
+import { fastestResult, najneomeskanejsiResult } from './selectAlgorithm';
|
|
| 1 |
+ |
|
| 2 |
+test('find fastest result', () => {
|
|
| 3 |
+ const fastest = fastestResult([ |
|
| 4 |
+ [ |
|
| 5 |
+ [ |
|
| 6 |
+ { processor: 1, endTime: 10 },
|
|
| 7 |
+ { processor: 1, endTime: 11 }
|
|
| 8 |
+ ], |
|
| 9 |
+ [ |
|
| 10 |
+ { processor: 2, endTime: 7 },
|
|
| 11 |
+ { processor: 2, endTime: 8 }
|
|
| 12 |
+ ] |
|
| 13 |
+ ], |
|
| 14 |
+ |
|
| 15 |
+ [ |
|
| 16 |
+ [ |
|
| 17 |
+ { processor: 1, endTime: 20 },
|
|
| 18 |
+ ], |
|
| 19 |
+ [ |
|
| 20 |
+ { processor: 2, endTime: 19 }
|
|
| 21 |
+ ] |
|
| 22 |
+ ], |
|
| 23 |
+ ]); |
|
| 24 |
+ |
|
| 25 |
+ expect(fastest).toBe(0); |
|
| 26 |
+}); |
|
| 27 |
+ |
|
| 28 |
+test('find najneomeskanejsi result', () => {
|
|
| 29 |
+ const best = najneomeskanejsiResult([ |
|
| 30 |
+ [ |
|
| 31 |
+ [ |
|
| 32 |
+ { processor: 1, endTime: 10, delayed: 10 },
|
|
| 33 |
+ { processor: 1, endTime: 11, delayed: 20 }
|
|
| 34 |
+ ], |
|
| 35 |
+ ], |
|
| 36 |
+ [ |
|
| 37 |
+ [ |
|
| 38 |
+ { processor: 1, endTime: 10, delayed: 0 },
|
|
| 39 |
+ { processor: 1, endTime: 11, delayed: 5 }
|
|
| 40 |
+ ], |
|
| 41 |
+ ], |
|
| 42 |
+ [ |
|
| 43 |
+ [ |
|
| 44 |
+ { processor: 1, endTime: 10, delayed: 10 },
|
|
| 45 |
+ { processor: 1, endTime: 11, delayed: 10 }
|
|
| 46 |
+ ], |
|
| 47 |
+ ] |
|
| 48 |
+ ]); |
|
| 49 |
+ |
|
| 50 |
+ expect(best).toBe(1); |
|
| 51 |
+}); |
|
| 0 | 52 |
\ No newline at end of file |