1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
|
//------------------------------------------------------------------------------
function TAutomateLogistiqueBusinessEngine.BuildMapTriFromSession(ASessionID: Integer; ASessionJour: Integer; ASessionVague: Integer; ASessionDropSide: Integer; ASessionNature: TxxxxEntitySDIDestinationNature.TDestinationNature; AEntityMap: TxxxxEntitySDIFileMap; const ATrieur: TTrieur; var ABuildResult: TBusinessBuildResultStruct; out AMap: TxxxxModuleAutomateLogistique.TRemoteMessageBusinessResponseMap): Boolean;
var
SessionDestinationList: TDestinationList;
TickStart, TickEnd: TLargeInteger;
itsdl: TDestinationListIterator;
OrderedDestinations: TArray<TDestinationListIterator>;
iDest, iDrop, iDropSide: Integer;
iDropBySide: System.Types.TIntegerDynArray;
Drop: ^TTrieurDropTri;
DestinationListOrderer: TDestinationListOrderer;
iFamily, FirstFamily: TxxxxEntitySDIFileMap.TMapFamily;
MapFamilies: TSessionFamilies;
FamillePlan: string;
ImplTMOverflow: Boolean;
ImplTMOverflowDestIndex: Integer;
iClone: Integer;
function CleanUnusedDestination(AMap: TxxxxEntitySDIFileMap): Boolean;
var
UsedDestinations: TDataSet;
iContainerType: TxxxxEntitySDIContainerType.TContainerType;
AllowedContainerTypes: array[TxxxxEntitySDIContainerType.TContainerType] of Integer;
begin
// Pour le Pré-Tri, on ne considère que certains types de contenant !
for iContainerType := Low(iContainerType) to High(iContainerType) do
begin
if iContainerType in CONTAINER_TYPE_TRI then
AllowedContainerTypes[iContainerType] := Ord(iContainerType)
else
AllowedContainerTypes[iContainerType] := -1;
end;
// Nettoyage des Destinations non utilisées en fonction de la famille
UsedDestinations := FDM.OpenDestinationParFamilleBySessionID(
ATrieur.TrieurID, ASessionID, AMap.MapFamilyCode.AsString,
AllowedContainerTypes[TxxxxEntitySDIContainerType.TContainerType.ctBLBR],
AllowedContainerTypes[TxxxxEntitySDIContainerType.TContainerType.ctCnPT],
AllowedContainerTypes[TxxxxEntitySDIContainerType.TContainerType.ctColis],
AllowedContainerTypes[TxxxxEntitySDIContainerType.TContainerType.ctInventaire]);
try
AMap.Items.First();
while not AMap.Items.EoF do
begin
// On supprime la destination (sauf si destination système)
if not UsedDestinations.Locate(FDM.OBJET_DESTINATION_TRI_CODE, AMap.Items.DropDestinationCode, []) and MatchStr(AMap.Items.DropDestinationCode, SYSTEM_DESTINATIONS) then
AMap.Items.Remove() // Remove supprime l'enregistrement actif et positionne l'ensemble de données sur l'enregistrement suivant.
else
AMap.Items.Next();
end;
AMap.DropAllocatedCount := AMap.Items.EntityCount;
Result := AMap.DropAllocatedCount > 0;
if not Result then
begin
ABuildResult.BuildResultCode := bbrSessionEmptyMap;
ABuildResult.BuildExtraMessage := 'Famille incorrecte : ' + AMap.MapFamilyCode.AsString;
Exit;
end;
finally
UsedDestinations.Close();
end;
end;
begin
Result := False;
QueryPerformanceCounter(TickStart);
// --- WARNING ---
// ---> Pour le moment, on NE gère PAS les destinations liées à une marque !
if ASessionNature <> TxxxxEntitySDIDestinationNature.TDestinationNature.dnTriMagasin then
raise EAutomateLogistiqueBusinessEngineError.CreateResFmt(@S_ERR_UNMANAGED_SESSION_NATURE_TRI, [TxxxxRTTI.EnumToString(TypeInfo(TxxxxEntitySDIDestinationNature.TDestinationNature), Ord(ASessionNature))]); // Pseudo-Assert Exception !
// * Phase 1 - Récupération des destinations FINALES !
SessionDestinationList := TDestinationList.Create([doOwnsValues]);
try
// On désactive par défaut la possibilité d'allouer une sortie pour toutes les destinations suplémentaires pour l'xxx xxx xxx
ImplTMOverflow := False;
// On n'accepte que les contenants pour le Tri et destinations de Tri !
if GetSessionDestinations(ATrieur, ASessionID, CONTAINER_TYPE_TRI, DESTINATION_NATURE_TRI, True, SessionDestinationList, ABuildResult) then
begin
// Vérification que cela ne dépasse pas la capacité de la machine
if SessionDestinationList.Count <= Length(ATrieur.Drops.Tri) then
begin
// Vérification que toutes les destinations FINALES sont planfiées sauf celle volontairement hors planning !
for itsdl in SessionDestinationList do
begin
if (itsdl.Value.Extra = detNone) and not IsPlanifiedDestination(ATrieur.Planning, itsdl.Value.DestinationCode, ASessionJour, ASessionVague) then
begin
ABuildResult.BuildResultCode := bbrSessionUnplanifiedDestination;
ABuildResult.BuildExtraMessage := 'Destination incorrecte : ' + itsdl.Value.DestinationCode;
Exit;
end;
end;
end
else
begin
ABuildResult.BuildResultCode := bbrSessionTooMoreDestination;
ABuildResult.BuildExtraMessage := Format('%d > %d', [SessionDestinationList.Count, Length(ATrieur.Drops.Tri)]);
// Seule une xxx xxx xxx peut dépasser la capacité en allouant une sortie pour toutes les destinations suplémentaires
for itsdl in SessionDestinationList do
begin
// Vérifie qu'il n'y a QUE de l'xxx xxx xxx pour chaque destination de cette session
if not (itsdl.Value.Extra in [detBLBRCommandexxxTM, detColisCommandexxxTM]) then
begin
// Si ce n'est pas un tri avec UNIQUEMENT de l'xxx xxx xxx, il faut émettre l'erreur SessionTooMoreDestination
// Recherche des contenants avec des destinations hors planning qui provoque le débordement de capacité !
BuildExtraMessageDestinationOverflowBySessionID(ATrieur.TrieurID, ASessionID, CONTAINER_TYPE_TRI, ABuildResult);
Exit;
end;
end;
// On a QUE de l'xxx xxx xxx mais il n'en faut pas de trop !
// Vérification que cela ne dépasse pas la capacité de la machine sur le SECOND TOUR !
// Il faut que le nombre de destination soit STRICTEMENT inférieur à deux fois la capacité machine puisqu'une sortie au 1er Tour a été allouée pour les destinations suplémentaires
if SessionDestinationList.Count < Length(ATrieur.Drops.Tri) * 2 then
begin
// On a QUE de l'xxx xxx xxx et ça tient en deux tours alors on peut continuer !
// On annule le code d'erreur pour continuer !
ABuildResult.BuildResultCode := bbrBuilding;
ABuildResult.BuildExtraMessage := '';
ImplTMOverflow := True;
end
else
begin
ABuildResult.BuildExtraMessage := ABuildResult.BuildExtraMessage + sLineBreak
+ 'Veuillez retirer des contenants d''xxx xxx xxx';
Exit;
end;
end;
end
else
Exit;
// Calcul du plan selon la quantité
OrderedDestinations := SessionDestinationList.ToArray();
DestinationListOrderer := TDestinationListOrderer.Create();
try
TArray.Sort<TDestinationListIterator>(OrderedDestinations, DestinationListOrderer);
finally
DestinationListOrderer.Free();
end;
// Est-il possible d'allouer une sortie pour toutes les destinations suplémentaires ?
if (Length(OrderedDestinations) > Length(ATrieur.Drops.Tri)) and ImplTMOverflow then
begin
SetLength(OrderedDestinations, Length(ATrieur.Drops.Tri));
// Même si ce n'est pas optimisé, on le place à la toute fin pour plus facilement retrouver ce contenant multi-magasin
// Si on le plaçait selon les quantités, il pourrait se trouver n'importe où sur la machine !
ImplTMOverflowDestIndex := High(ATrieur.Drops.Tri);
OrderedDestinations[ImplTMOverflowDestIndex].Key := DESTINATION_IMPL_TM_TOO;
OrderedDestinations[ImplTMOverflowDestIndex].Value := TDestinationInfo.Create(DESTINATION_IMPL_TM_TOO, DESTINATION_IMPL_TM_TOO_TEXT, Ord(TxxxxEntitySDIDestinationNature.TDestinationNature.dnTriTiers), 0);
end;
// Récupération des familles présentes dans l'ensemble des contenants de la session
if not GetFamiliesOfSession(ASessionID, CONTAINER_TYPE_TRI, ABuildResult, MapFamilies) then
Exit;
// Vérification des Doublons !
if not CheckDuplicateObjetInSession(ASessionID, CONTAINER_TYPE_TRI, ABuildResult) then
Exit;
// Quelle est la 1ère famille disponible ?
FirstFamily := TxxxxEntitySDIFileMap.TMapFamily.mfUndefined;
for iFamily := Low(iFamily) to High(iFamily) do
begin
if iFamily in MapFamilies then
begin
FirstFamily := iFamily;
Break;
end;
end;
if FirstFamily = TxxxxEntitySDIFileMap.TMapFamily.mfUndefined then
begin
ABuildResult.BuildResultCode := bbrSessionContenantBadFamily;
Exit;
end;
// Calcul du plan
if Length(ATrieur.Drops.Sides) > 0 then
begin
// Calcul de la 1ere chute de chaque côté
iDropBySide := Copy(ATrieur.Drops.SideFirstIndexes, 0, MaxInt);
AEntityMap.MapStateValue.IsNull := True;
AEntityMap.TrieurID.AsInteger := ATrieur.TrieurID;
if ASessionNature = TxxxxEntitySDIDestinationNature.TDestinationNature.dnTriMagasin then
begin
AEntityMap.MapFamily := FirstFamily;
FamillePlan := AEntityMap.ConvertToMapFamilyCode(FirstFamily);
AEntityMap.MapFriendlyName := Format('Tri %s de la Session N°%.8d', [FamillePlan, ASessionID]) // Tri xx de la Session N°00000000 = 31 caractères !
end
else
begin
AEntityMap.MapFamilyCode.IsNull := True;
AEntityMap.MapFriendlyName := 'Tri de la Session N°' + Format('%.8d', [ASessionID]); // Tri de la Session N°00000000 = 28 caractères !
end;
AEntityMap.DropType := sdidtTri;
AEntityMap.DropAllocatedCount := Length(OrderedDestinations);
AEntityMap.DropAssignedRequested := DropPreAssignationIsRequested();
SetLength(AMap.Items, Length(OrderedDestinations));
// Affectation des destinations d'un côté à l'autre en alternance ou d'un premier côté puis à l'autre
if ASessionDropSide = 0 then
iDropSide := Low(ATrieur.Drops.Sides)
else
iDropSide := ASessionDropSide - 1;
if (Low(ATrieur.Drops.Sides) <= iDropSide) and (iDropSide <= High(ATrieur.Drops.Sides)) then
begin
iDrop := iDropBySide[iDropSide];
end
else
begin
ABuildResult.BuildResultCode := bbrSessionTriBadSide;
ABuildResult.BuildExtraMessage := 'Valeur incorrecte : ' + IntToStr(iDropSide);
Exit;
end;
for iDest := Low(OrderedDestinations) to High(OrderedDestinations) do
begin
Drop := @ATrieur.Drops.Tri[iDrop];
AEntityMap.Items.Append();
AEntityMap.Items.DropNumber := Drop.DropNumber;
AEntityMap.Items.DropVolume := Drop.DropVolume;
AEntityMap.Items.DropDestinationCode := OrderedDestinations[iDest].Value.DestinationCode;
AEntityMap.Items.DropDestinationFriendlyName := OrderedDestinations[iDest].Value.DestinationFriendlyName;
AEntityMap.Items.ContainerOutputPrefix.AsString := '';
AMap.Items[iDest].DropNumber := Drop.DropNumber;
AMap.Items[iDest].DestinationCode := AnsiString(OrderedDestinations[iDest].Value.DestinationCode);
AMap.Items[iDest].Quantity := OrderedDestinations[iDest].Value.Quantity;
if ASessionDropSide = 0 then
begin
// d'un côté à l'autre en alternance
Inc(iDropBySide[iDropSide]);
Inc(iDropSide);
if iDropSide > High(ATrieur.Drops.Sides) then
iDropSide := Low(ATrieur.Drops.Sides);
iDrop := iDropBySide[iDropSide];
// Vérifie que l'on ne dépasse pas d'un côté (cela peut se produire sur un plan non équilibré comme celui du 14 Avril 2015 PT:01..20 et MG:021..100
if iDrop > ATrieur.Drops.SideLastIndexes[iDropSide] then
begin
// Si l'on dépasse alors on retourne sur l'autre côté
Inc(iDropSide);
if iDropSide > High(ATrieur.Drops.Sides) then
iDropSide := Low(ATrieur.Drops.Sides);
iDrop := iDropBySide[iDropSide];
// Vérifie que l'on ne dépasse pas non plus de ce côté
if iDrop > ATrieur.Drops.SideLastIndexes[iDropSide] then
begin
// On ne déclenche une erreur QUE si c'est pas le dernier, évidemment au dernier ça dépasse puisque le suivant n'existe pas !
if iDest < High(OrderedDestinations) then
begin
ABuildResult.BuildResultCode := bbrSessionTriNotFoundFreeSide;
ABuildResult.BuildExtraMessage := 'Valeurs incorrectes : ' + IntToStr(iDropSide) + ' / ' + IntToStr(iDrop);
Exit;
end;
end;
end;
end
else
begin
// d'un premier côté puis à l'autre
Inc(iDrop);
if iDrop > High(ATrieur.Drops.Tri) then
begin
Inc(iDropSide);
if iDropSide > High(ATrieur.Drops.Sides) then
iDropSide := Low(ATrieur.Drops.Sides);
iDrop := iDropBySide[iDropSide];
end
else
begin
// On peut changer de côté juste en augmentant l'Index
iDropSide := IndexOfSide(ATrieur.Drops, ATrieur.Drops.Tri[iDrop].DropSide);
end;
// Vérifie que l'on ne dépasse pas sur ce côté
if iDrop > ATrieur.Drops.SideLastIndexes[iDropSide] then
begin
// On ne déclenche une erreur QUE si c'est pas le dernier, évidemment au dernier ça dépasse puisque le suivant n'existe pas !
if iDest < High(OrderedDestinations) then
begin
ABuildResult.BuildResultCode := bbrSessionTriNotFoundNextFreeSide;
ABuildResult.BuildExtraMessage := 'Valeurs incorrectes : ' + IntToStr(iDropSide) + ' / ' + IntToStr(iDrop);
Exit;
end;
end;
end;
end;
QueryPerformanceCounter(TickEnd);
AEntityMap.Meta.FileState := TxxxxEntitySDIFile.TFileState.sdifsWaiting;
AEntityMap.Meta.BuildDuration.AsInteger := Trunc((TickEnd - TickStart) / FTickPerSec * MSecsPerSec);
// Plan cloné !
for iFamily := Succ(FirstFamily) to High(iFamily) do
begin
if iFamily in MapFamilies then
begin
FamillePlan := AEntityMap.ConvertToMapFamilyCode(iFamily);
AEntityMap.MakeClone(iFamily, Format('Tri %s de la Session N°%.8d', [FamillePlan, ASessionID])); // Tri xx de la Session N°00000000 = 31 caractères !
end;
end;
// Nettoyage des Destinations non utilisées en fonction de la famille
if CleanUnusedDestination(AEntityMap) then
begin
for iClone := 0 to AEntityMap.Clones.Count - 1 do
if not CleanUnusedDestination(AEntityMap.Clones[iClone]) then
Exit;
end
else
Exit;
end
else
begin
ABuildResult.BuildResultCode := bbrSessionTriNoSides;
Exit;
end;
finally
SessionDestinationList.Free();
end;
Result := True;
end; |
Partager