algoritmo di posizionamento verticale gantt con ottimizzazione delle posizioni in riga.

This commit is contained in:
=
2020-06-25 16:44:32 +02:00
parent 2dc5ca8ebe
commit b0684fa03f
7 changed files with 105 additions and 40 deletions
@@ -45,7 +45,11 @@ export default class GanttComponent extends Vue {
return this.value.showDelay ? Math.max(this.value.estimatedDelay / 1000, this.ganttOptions.block_delay_minDuration) : 0;
}
mounted() {
this.value.showDelay = true;
get statusDuration() {
return this.showStatus ? this.ganttOptions.block_status_minDuration : 0;
}
// mounted() {
// this.value.showDelay = true;
// }
}
@@ -2,7 +2,7 @@
<svg
class="gantt-block"
:class="blockType"
:width="ganttOptions.secondSize * (delayDuration + duration)"
:width="ganttOptions.secondSize * (delayDuration + duration + statusDuration)"
:height="ganttOptions.elementHeight"
>
<rect x="0" y="0" width="100%" height="70" class="body" />
@@ -34,8 +34,26 @@
<foreignObject :width="duration * ganttOptions.secondSize" height="26" x="0" y="44">
<div class="body-footer">{{(value.estimatedDuration / 1000) | round(1)}}s</div>
</foreignObject>
<line
v-if="showStatus"
class="vertical-line"
:x1="duration * ganttOptions.secondSize"
:x2="duration * ganttOptions.secondSize"
:y1="0"
:y2="70"
/>
</g>
<g
v-if="showStatus"
:transform="`translate(${(delayDuration + duration) * ganttOptions.secondSize} 0)`"
>
<foreignObject :width="statusDuration * ganttOptions.secondSize" height="44" x="0" y="0">
<div class="status-header">{{value.label | localize(value.label)}}</div>
</foreignObject>
<foreignObject :width="statusDuration * ganttOptions.secondSize" height="26" x="0" y="44">
<div class="status-footer">{{(value.estimatedDuration / 1000) | round(1)}}s</div>
</foreignObject>
</g>
<g v-if="showStatus" />
</svg>
</template>
<style scoped>
@@ -3,6 +3,7 @@ import Vue from "vue";
import { Prop, InjectReactive } from "vue-property-decorator";
import { IGanttOptions } from "./gantt";
import block from "./gantt-component.vue";
import { relativeTimeRounding } from "moment";
@Component({ components: { block } })
export default class GanttRow extends Vue {
@@ -26,19 +27,31 @@ export default class GanttRow extends Vue {
return blockStartPosition(block, this.blocks, this.ganttOptions) * this.ganttOptions.secondSize;
}
verticalPosition(block: server.Modblock) {
/*
Questa funzione calcola la posizione verticale di ogni blocco seguendo l'ordine dei blocchi ed utilizza una variabile
per memorizzare temporaneamente la posizione assegnata ad ogni blocco.
*/
verticalPosition(block: server.Modblock, rows: { id: number, from: number, to: number }[][] = [], recurring: boolean = false) {
let idx = this.blocksInSection.indexOf(block);
let myStartPosition = this.startPosition(block);
let result = this.blocksInSection.slice(0, idx).filter(p => {
let sp = this.startPosition(p);
let ep = sp + p.estimatedDuration / 1000;
// calcolo le posizioni dei predecessori con una ricorsione solo se non sono già in una ricorsione..
if (idx && !recurring)
for (const b of this.blocksInSection.slice(0, idx)) this.verticalPosition(b, rows, true);
return sp <= myStartPosition && ep >= myStartPosition;
}).length * (this.ganttOptions.elementHeight + this.ganttOptions.elementPadding * 2);
// calcolo la posizione orizzontale del mio blocco.
let myStartPosition = blockStartPosition(block, this.blocks, this.ganttOptions);
let myEndPosition = myStartPosition + blockDuration(block, this.ganttOptions, true);
// cerco la prima delle righe disponibili che avrebbero posto per il mio blocco
let myrow = rows.filter(r => r.filter(b => (b.from >= myStartPosition && b.from <= myEndPosition) || (b.to >= myStartPosition && b.to <= myEndPosition)).length == 0).shift();
if (!myrow) rows.push(myrow = []);
return result;
// inserisco il mio blocco per occupare la posizione
myrow.push({ from: myStartPosition, to: myEndPosition, id: block.id });
// la posizione verticale è al posizione della riga nell'elenco delle righe.
return rows.indexOf(myrow) * (this.ganttOptions.elementHeight + this.ganttOptions.elementPadding * 2);
}
get rowHeight() {
@@ -49,24 +62,34 @@ export default class GanttRow extends Vue {
function blockEndPosition(block: server.Modblock, blocks: server.Modblock[], options: IGanttOptions) {
let ids = block.precedingId.filter(i => i > 0);
let duration = Math.max(0, ...blocks.filter(i => ids.indexOf(i.id) >= 0).map(i => blockEndPosition(i, blocks, options)));
return duration + blockDuration(block, options, false);
}
// check della durata del blocco se si vede il ritardo
function blockDuration(block: server.Modblock, options: IGanttOptions, includeDelay: boolean) {
let duration = 0;
if (block.showDelay)
duration += Math.max(block.estimatedDelay, options.block_delay_minDuration) / 1000;
duration += Math.max(block.estimatedDelay / 1000, includeDelay ? options.block_delay_minDuration : 0);
// check della durata del blocco in base al tipo
var minduration = 0;
switch (block.type) {
case 1: minduration = options.block_body_Heating_minDuration;
case 2: minduration = options.block_body_Drawing_minDuration;
case 3: minduration = options.block_body_Movement_minDuration;
case 4: minduration = options.block_body_Vacuum_minDuration;
case 5: minduration = options.block_body_Cooling_minDuration;
case 6: minduration = options.block_body_Extraction_minDuration;
}
return duration += Math.max(minduration, block.estimatedDuration) / 1000;
if (includeDelay)
switch (block.type) {
case 1: minduration = options.block_body_Heating_minDuration;
case 2: minduration = options.block_body_Drawing_minDuration;
case 3: minduration = options.block_body_Movement_minDuration;
case 4: minduration = options.block_body_Vacuum_minDuration;
case 5: minduration = options.block_body_Cooling_minDuration;
case 6: minduration = options.block_body_Extraction_minDuration;
}
duration += Math.max(minduration, block.estimatedDuration / 1000);
// return duration + (block.estimatedDuration / 1000);
if (block.idParam > 0 && includeDelay)
duration += options.block_status_minDuration;
if (includeDelay)
duration += options.block_padding;
return duration;
}
function blockStartPosition(block: server.Modblock, blocks: server.Modblock[], options: IGanttOptions) {
@@ -9,14 +9,17 @@
.gantt-block .delay-header,
.gantt-block .delay-footer,
.gantt-block .body-footer,
.gantt-block .body-header {
.gantt-block .body-header,
.gantt-block .status-footer,
.gantt-block .status-header {
height: 100%;
width: 100%;
display: flex;
width: calc(100% - 10px);
}
.gantt-block .delay-header,
.gantt-block .body-header {
.gantt-block .body-header,
.gantt-block .status-header {
height: calc(100% - 10px);
padding: 5px;
align-items: flex-end;
@@ -24,24 +27,29 @@
color: #545454;
}
.gantt-block .delay-footer,
.gantt-block .body-footer {
.gantt-block .body-footer,
.gantt-block .status-footer {
align-items: center;
justify-content: flex-start;
color: #fff;
padding: 0 5px;
}
.gantt-block .body-header {
.gantt-block .body-header,
.gantt-block .status-header {
align-items: center;
justify-content: center;
}
.gantt-block .body-footer {
.gantt-block .body-footer,
.gantt-block .status-footer {
justify-content: center;
}
.gantt-block .body {
color: #fff;
rx: 6;
}
.gantt-block .header {
color: #545454;
rx: 4;
}
.gantt-block.heating .body {
fill: #913030;
@@ -10,7 +10,9 @@
.delay-header,
.delay-footer,
.body-footer,
.body-header {
.body-header,
.status-footer,
.status-header {
height: 100%;
width: 100%;
display: flex;
@@ -18,7 +20,8 @@
}
.delay-header,
.body-header {
.body-header,
.status-header {
height: calc(~'100% - 10px');
padding: 5px;
align-items: flex-end;
@@ -27,28 +30,33 @@
}
.delay-footer,
.body-footer {
.body-footer,
.status-footer {
align-items: center;
justify-content: flex-start;
color: #fff;
padding: 0 5px;
}
.body-header {
.body-header,
.status-header {
align-items: center;
justify-content: center;
}
.body-footer {
.body-footer,
.status-footer {
justify-content: center;
}
.body {
color: #fff;
rx: 6;
}
.header {
color: #545454;
rx: 4;
}
&.heating {
@@ -14,6 +14,9 @@ export default class Gantt extends Vue {
@Prop()
blocks: server.Modblock[];
padX: number = 0;
padY: number = 0;
@ProvideReactive()
ganttOptions: IGanttOptions = {
width: 2000,
@@ -23,7 +26,7 @@ export default class Gantt extends Vue {
stepDuration: 2,
labelInterval: 10,
maxGanttDuration: 500,
block_status_minDuration: 1.5,
block_status_minDuration: 4,
block_delay_minDuration: 1.5,
block_body_Heating_minDuration: 5,
block_body_Drawing_minDuration: 5,
@@ -31,6 +34,7 @@ export default class Gantt extends Vue {
block_body_Vacuum_minDuration: 5,
block_body_Cooling_minDuration: 5,
block_body_Extraction_minDuration: 5,
block_padding: 0.01
};
currentDate: Date = null;
@@ -44,9 +48,6 @@ export default class Gantt extends Vue {
}
}
export interface IGanttElement {
from: Date,
to?: Date,
@@ -70,4 +71,5 @@ export interface IGanttOptions {
block_body_Vacuum_minDuration: number;
block_body_Cooling_minDuration: number;
block_body_Extraction_minDuration: number;
block_padding: number;
}
@@ -1,11 +1,12 @@
<template>
<svg ref="mainContainer" preserveAspectRation="xMinYMax" width="100%" height="100%">
<gantt-header />
<gantt-header :padding-horizontal="padX" />
<g :transform="`translate(0 ${ganttOptions.stepDuration * ganttOptions.secondSize})`">
<gantt-row :section="1" :blocks="blocks" ref="section1">
<gantt-row :section="1" :blocks="blocks" ref="section1" :padding-horizontal="padX">
<span>{{`process-heating` | localize('Riscaldo')}}</span>
</gantt-row>
<gantt-row
:padding-horizontal="padX"
:section="2"
:blocks="blocks"
ref="section2"
@@ -14,6 +15,7 @@
<span>{{`process-forming` | localize('Formatura')}}</span>
</gantt-row>
<gantt-row
:padding-horizontal="padX"
:section="3"
:blocks="blocks"
:transform="`translate(0 ${getRowHeight($refs.section1) + getRowHeight($refs.section2) + ganttOptions.elementPadding*2})`"