using CP;
int S = 6; // Number of stations
int G = 2; // Number of workstations per station
int c = 120; // Cycle time
int StationLength=120;
int Arbeitsplaetze=2;
int MaxDrift[1..S][1..G]=...; //drift
int maxOverload=30;
{string} Variante={"V","E","G"};
int d[Variante] = [1,1,1]; //Demand of variants
int N = sum(v in Variante) d[v]; //Instance of product
int l=card(Variante);
int nb2[1..G,1..S*card(Variante)]=...; //Input processing times
int b[v in Variante][s in 1..S][g in 1..G] = nb2[g,1+ord(Variante,v)*S+(s-1)]; //processing time at each station and workstation
int v[i in 1..N] =0;
execute {
var i=1;
for(var j=1; j<=l; j++) {
for(var k=1; k<=d[Opl.item(Variante,j-1)]; k++) {
v[i++]=j;
}
}
}
// Decision variables
dvar interval station[i in 1..N][s in 1..S][g in 1..G] in ((s==1) ? ((i+s-2)*120) : (((i+s-2)*120)-MaxDrift[s-1][g]))..(((i+s-1)*120)+MaxDrift[s][g]) size ((s==1) ? StationLength : StationLength-MaxDrift[s-1][g])..StationLength+MaxDrift[s][g]; //Available length of each workplace
dvar interval work[i in 1..N][s in 1..S][g in 1..G] size (b[item(Variante,v[i]-1)][s][g]-maxOverload)..b[item(Variante,v[i]-1)][s][g]; //Length of working time minus if overload occurs
dvar sequence stationSeq[s in 1..S][g in 1..G] in all(i in 1..N) station[i][s][g]; //Sequence variables for stations
dvar sequence workStationSeq[s in 1..S][g in 1..G] in all(i in 1..N) work[i][s][g]; //Sequence variables for working
execute {
cp.param.TimeLimit = 300; //time limit
//var f = cp.factory;
//cp.setSearchPhases(f.searchPhase(stationSeq[1])); //it can improve the performance of the search
}
dexpr int overload [i in 1..N][s in 1..S][g in 1..G] = b[item(Variante,v[i]-1)][s][g]-sizeOf(work[i][s][g]);
dexpr int makespan = max(i in 1..N, g in 1..G) endOf(work[i][S][g]);
minimize sum(i in 1..N, s in 1..S, g in 1..G) overload[i][s][g];
subject to {
makespan<=(S+(N-1))*c;
forall(i in 1..N) {
//startOf(station[i][1][1]) % c == 0; //The start time at the first station, ie at both workplaces, can start at 0,120,240,360, ... at the earliest. the starting time of the very first product is set to 0.
//startOf(station[i][1][2]) % c == 0;
startOf(station[1][1][2]) == 0;
startOf(station[1][1][2]) == 0;
endOf(station[i][S][1])<=(S+(i-1))*c;
endOf(station[i][S][2])<=(S+(i-1))*c;
forall(s in 1..S, g in 1..G) {
if (s > 1) {
endBeforeStart(station[i][s-1][g],station[i][s][g]); //The workplace can start working at the earliest when the upstream workplace no longer works on the product
}
forall(g in 1..G) {
startAtStart(work[i][s][g],station[i][s][g]);
endBeforeEnd(work[i][s][g],station[i][s][g]);
}
}
}
forall(i in 1..N, s in 2..S, g in 1..G) {
endBeforeStart(work[i][s-1][g],work[i][s][g]); } //The processing time can start at the earliest when the preceding processing time either ended successfully or the work could not be completed because overload occurs.
forall(s in 1..S, g in 1..G) {
noOverlap(stationSeq[s][g]); //since it is a conveyor belt, the stations can not "overtake"
if (s > 1) {
sameSequence(stationSeq[1][g],stationSeq[s][g]); //every station has the same sequence
}
forall(g in 1..G) {
noOverlap(workStationSeq[s][g]); //every workplace has the same sequence
sameSequence(stationSeq[1][g],workStationSeq[s][g]);
}
}
forall(g in 2..G) {
sameSequence(stationSeq[1][1],stationSeq[1][g]);
}
}
tuple Solution { int start; int product; {string} Variante; }
sorted {Solution} sol = { <startOf(station[i][1][1]),i,{j} > | i in 1..N,j in Variante };
execute {
writeln(sol);
}
;