import { find, map, reduce, sort, filter, flatten, reject, contains, omit, forEach, all } from 'rambda'; import type { JobId } from '../../types'; import { groupBy } from 'ramda'; import { updateCTime } from '../mcnaught'; type JobTime = { jobId: JobId, t: number, name: string, anc: JobId[] }; const noSuccJobs = (jobTimes: JobTime[]) => { const allAnc = flatten(map(jobTime => jobTime.ignore ? [] : jobTime.anc, jobTimes)); return reject(jobTime => jobTime.ignore || contains(jobTime.jobId, allAnc), jobTimes); }; const successors = (id: JobId, jobTimes: JobTime[]) => filter(({ anc }) => contains(id, anc), jobTimes); const fillCorrupted = (jobs: JobTime[]) => map( ({ anc, ...rest }) => ({ ...rest, anc: anc || [] }), jobs); export const createSchedule = (jobTimes: JobTime[], m: number) => { jobTimes = fillCorrupted(jobTimes); let T = reduce((acc, jobTime) => acc + jobTime.t, 0, jobTimes); let noSucc = noSuccJobs(jobTimes); noSucc[0].w = reduce((acc, job) => acc + job.w, 0, noSucc[0].anc) + noSucc[0].t; forEach((jobTime: JobTime) => { jobTime.wCalc = () => jobTime.t + reduce((acc, aJob) => acc + aJob.wCalc(), 0, successors(jobTime.jobId, jobTimes)) }, jobTimes); forEach((jobTime: JobTime) => { jobTime.w = jobTime.wCalc() }, jobTimes); let c = Math.floor(T / m) + Math.sign(T - m * Math.floor(T / m)); let assignedIds; let jobTimesSorted = sort((a, b) => b.w - a.w, jobTimes); while (c <= T) { assignedIds = []; let activeProcessor = 1; // console.log('procesor', activeProcessor); while (activeProcessor <= m) { let restC = c; let lastActiveProcessor = activeProcessor; forEach(withMaxW => { if (contains(withMaxW.jobId, assignedIds)) { // console.log('already assigned', withMaxW.jobId); return; } let wereAllAncAssigned = all(jobId => contains(jobId, assignedIds), withMaxW.anc); if (!wereAllAncAssigned) { // console.log('nemozem priradit', withMaxW.jobId); return; } if (withMaxW.t <= restC) { withMaxW.processor = activeProcessor; assignedIds.push(withMaxW.jobId); restC -= withMaxW.t; // console.log('priradil som', withMaxW.jobId); } else { // console.log('nezmesti sa', withMaxW.jobId); } }, jobTimesSorted); if (!restC || find(jobTime => jobTime.t <= restC && !contains(jobTime.jobId, assignedIds), jobTimesSorted)) { activeProcessor++; // console.log('dalsi procesor', activeProcessor); } if (activeProcessor === lastActiveProcessor) { // console.log('zvys takt'); break; } } if (assignedIds.length === jobTimesSorted.length) { break; } // console.log('dalsi takt', c+1); c++; } if (assignedIds.length !== jobTimesSorted.length) { return null; } return updateCTime( map(omit(['wCalc', 'anc', 'w']), jobTimesSorted) ); }; export default (jobTimes: JobTime[], processorsCount: number) => { const schedule = createSchedule(jobTimes, processorsCount); if (!schedule) return null; const grouped = Object.values(groupBy(jobTime => jobTime.processor, schedule)); return map(map(job => ({ processor: job.processor, startTime: job.c - job.t, endTime: job.c, name: job.name })), grouped); };