UNIVERSITEIT GENT Faculteit Toegepaste Wetenschappen ____________________
Vakgroep Technische Bedrijfsvoering Voorzitter: Prof. Dr. Ir. R. Van Landeghem
Generieke simulator voor magazijnoptimalisatie door Jurgen De Keyser
Promotor : Prof. Dr. Ir. R. Van Landeghem Scriptiebegeleider: Ing. J. Soons
Scriptie ingediend tot het behalen van de academische graad van burgerlijk werktuigkundig-elektrotechnisch ingenieur
Academiejaar 2002-2003
Voorwoord
Bij de verwezenlijking van deze scriptie heb ik op de steun kunnen rekenen van een aantal mensen die ik bij deze wil bedanken. In de eerste plaats dank ik Prof. Dr. Ir. R. Van Landeghem voor de begeleiding bij het tot stand komen van dit werk en het ter beschikking stellen van het pc-lokaal. Daarnaast dank ik ook Ing. J. Soons voor de begeleiding inzake het programmeren. Dankzij dit eindwerk heb ik mijn basiskennis op het vlak van logistiek, warehousing, routeringsalgoritmen en programmeren in Visual Basic 6.0 sterk kunnen verruimen. Tot slot wil ik deze scriptie ook opdragen aan mijn ouders die mij de voorbije vijf jaar steeds moreel en financieel gesteund hebben.
De auteur geeft de toelating deze scriptie voor consultatie beschikbaar te stellen en delen ervan te kopiëren voor persoonlijk gebruik. Elk ander gebruik valt onder de beperkingen van het autersrecht, in het bijzonder met betrekking tot de verplichting de bron uitdrukkelijk te vermelden bij het aanhalen van resultaten uit deze scriptie. Mei, 2003
Jurgen De Keyser
Overzicht
Generieke simulator voor magazijnoptimalisatie door Jurgen De Keyser Scriptie ingediend tot het behalen van de academische graad van burgerlijk werktuigkundig-elektrotechnisch ingenieur Academiejaar 2002-2003 Promotor: Prof. Dr. Ir. R. Van Landeghem Scriptiebegeleider: Ing. J. Soons Faculteit Toegepaste Wetenschappen Universiteit Gent Vakgroep Technische Bedrijfsvoering Voorzitter: Prof. Dr. Ir. R. Van Landeghem
Samenvatting Het doel van deze scriptie is het ontwerpen van een programma dat in staat is om, op basis van informatie betreffende de layout en de materiaalbehandeling en gegeven de orderspecificaties, de orderverzamelactiviteiten binnen een magazijn te simuleren, waarbij telkens tussen twee gegeven opeenvolgende punten het kortste pad wordt gezocht. Als programmeeromgeving gebruiken we Visual Basic 6.0. Input en output gebeurt met Excel-files In het eerste deel van deze scriptie bespreken we de functies en de layout van magazijnen en de karakteristieken van opslagsystemen en intern transportmaterieel. Ook besteden we aandacht aan de karakteristieken van het orderverzamelen. Magazijnen en distributiecentra nemen nu en zullen ook in de toekomst een belangrijke plaats innemen in de logistieke keten. Wel zullen nieuwe diensten en activiteiten uitgevoerd worden en bepaalde taken zullen aan belang winnen. Centraal daarbij staat het steeds efficiënter uitvoeren van deze taken. Vooral op het vlak van het orderverzamelen levert men veel inspanningen door aangepaste layouts en besturingssystemen van de magazijnen te introduceren. Om voor de fysieke implementatie van deze zaken toch een idee te krijgen van het effect van deze maatregelen zoekt men meer en meer zijn toevlucht tot simulatie. In hoofdstuk 3 gaan we dieper in op het werken met Visual Basic 6.0 en Excel. De opbouw van de simulator komt aan bod in hoofdstuk 4. In hoofdstuk 5 bespreken we het gebruikte routeringsalgoritme. Tot slot besteden we aandacht aan het uitvoeren van de simulaties. Trefwoorden: magazijnen, Visual Basic, Excel, magazijnsimulator, kortste pad algoritme van Dijkstra
Inhoudstafel
Hoofdstuk 1: Magazijnen en het belang van simulatie 1 Inleiding……………………………………………………………………………. 1 2 Efficiëntie van het orderverzamelen……………………………………………….. 3 3 Simulatie…………………………………………………………………………… 3 Hoofdstuk 2: Organisatie van magazijnen en order picking 1 Inleiding……………………………………………………………………………. 5 1.1 Functies van een distributiecentrum……………………………………….. 5 1.2 Functionaliteiten van het distributiecentrum………………………………. 6 1.2.1 Functionele eisen aan distributiecentra…………………………….. 6 1.2.2 Value Added Logistics……………………………………………...7 1.2.3 Assemblage-activiteiten……………………………………………. 8 1.2.4 Locatie van het distributiecentrum………………………………….8 1.3 De hoofdprocessen in het magazijn………………………………………... 8 1.3.1 Ontvangst van de goederen (inslag)………………………………... 8 1.3.2 Verzenden naar de opslag………………………………………….. 9 1.3.3 Opslag……………………………………………………………… 9 1.3.4 Uitslag……………………………………………………………… 9 1.3.5 Orderverzamelen…………………………………………………… 10 1.3.6 Verpakking, laden en verzenden…………………………………… 10 1.3.7 Informatieverwerking……………………………………………… 10 1.3.8 Cross-docking……………………………………………………… 11 1.3.9 Overzicht…………………………………………………………… 11 1.4 Soorten magazijnen………………………………………………………... 12 1.5 Layout van een magazijn…………………………………………………... 14 2 Organisatie van de opslag………………………………………………………….. 16 2.1 Opslagpolitieken…………………………………………………………… 16 2.2 (Eenheids)ladingdragers…………………………………………………… 17 2.3 Opslagsystemen……………………………………………………………. 18 2.3.1 Blokstapeling………………………………………………………. 19 2.3.2 Palletstellingen……………………………………………………... 19 2.3.3 Legbordstellingen………………………………………………….. 20 2.3.4 Doorrolstellingen…………………………………………………... 21 2.3.5 Paternosteropslag…………………………………………………... 22 2.3.6 Draagarmstellingen………………………………………………… 23 2.3.7 Insteekstellingen…………………………………………………… 23 2.3.8 AS/RS hoogtemagazijn…………………………………………….. 24 3 Intern transportmaterieel…………………………………………………………… 25 3.1 Soorten transportmiddelen…………………………………………………. 25 3.1.1 Vorkheftruck of contragewichttruck……………………………….. 25 3.1.2 Reachtruck of smalle gangen truck………………………………… 26 3.1.3 Pallettruck………………………………………………………….. 27 3.1.4 Stapelaar…………………………………………………………….27 Inhoudstafel
i
4
3.1.5 Automatic Guided Vehicle (AGV)………………………………… 28 3.2 Overzicht………………………………………………………………….... 29 Order picking………………………………………………………………………. 29 4.1 Het orderverzamelproces……………………………………………………30 4.2 Orderverzamelstrategieën………………………………………………….. 31 4.2.1 Eéntraps- of ordergeoriënteerde verzamelmethode………………... 31 4.2.2 Meertraps- of artikelgeoriënteerde verzamelmethode……………... 31 4.2.3 Wave-picking (Pick, sort en pack)…………………………………. 32 4.3 Principes van orderverzamelen…………………………………………….. 32 4.4 Componenten van de orderverzameltijd of picktijd……………………….. 33 4.5 Technieken voor efficiënter informatieverwerking bij het picken………… 35
Hoofdstuk 3: Gebruik van Visual Basic 6.0 en Microsoft Excel 1 Inleiding……………………………………………………………………………. 36 2 Algemene opbouw van een Visual Basic-programma……………………………... 37 3 Algemene opbouw van een module………………………………………………... 37 3.1 Declaratie constanten, variabelen en externe functies (declaratiegedeelte)...38 3.1.1 Constanten……………………………………………….………….38 3.1.2 Variabelen………………………………………………………….. 38 3.1.3 Externe functies en procedures…………………………………….. 40 3.2 Opbouw en oproep van een procedure (proceduregedeelte)……………….. 40 4 Class modules en objecten…………………………………………………………. 41 4.1 Definities…………………………………………………………………… 41 4.2 Opbouw van een Class Module……………………………………………. 41 4.3 Class Diagram (in UML)…………………………………………………... 42 5 Grafische methoden in Visual Basic……………………………………………….. 44 6 Microsoft Excel Object…………………………………………………………….. 46 6.1 Definitie DOM……………………………………………………………... 46 6.2 Gebruik van Excel Object Model………………………………………….. 46 6.2.1 Excel Application Object…………………………………………... 47 6.2.2 Workbook Object…………………………………………………... 47 6.2.3 Worksheet Object…………………………………………………...47 6.2.4 Range Object………………………………………………………..48 Hoofdstuk 4: Opbouw magazijnsimulator 1 Inleiding……………………………………………………………………………. 51 2 Veronderstellingen en concept achter magazijnsimulator…………………………. 51 2.1 Veronderstellingen…………………………………………………………. 51 2.1.1 Magazijnlayout…………………………………………………….. 51 2.1.2 Orderverzamelen…………………………………………………… 53 2.1.3 Routering……………………………………………………………53 2.1.4 Infrastructuur voor materiaalbehandeling………………………….. 55 2.2 Concept…………………………………………………………………….. 56 3 Input………………………………………………………………………………... 56 3.1 layout………………………………………………………………………..57 3.2 knopen……………………………………………………………………… 58 3.3 orders………………………………………………………………………..59 Inhoudstafel
ii
4 5
6
7
8
9 10
11
12
13
3.4 transport……………………………………………………………………..59 3.5 netwerk……………………………………………………………………...60 3.6 schema1, schema2, schema3, schema4……………………………………..61 Class Diagram Magazijnsimulator………………………………………………….61 Klasse Invoer (Invoer.cls)………………………………………………………….. 62 5.1 Opbouw…………………………………………………………………….. 62 5.2 Instellingen………………………………………………………………….65 Klasse Magazijn (Magazijn.cls)…………………………………………………….65 6.1 Opbouw…………………………………………………………………….. 65 6.2 Instellingen………………………………………………………………….69 Klasse Order (Order.cls)…………………………………………………………… 69 7.1 Opbouw…………………………………………………………………….. 69 7.2 Instellingen………………………………………………………………….70 Klasse Route (Route.cls)……………………………………………………………70 8.1 Opbouw…………………………………………………………………….. 70 8.1.1 Methoden en procedures van toepassing bij de initialisatie van het Route-object……………………………………………………. 71 8.1.2 Methoden enkel van toepassing in het hoofdprogramma…………...73 8.2 Instellingen………………………………………………………………….74 Klasse Knoop (Knoop.cls)…………………………………………………………. 74 Klasse Dijkstra (Dijkstra.cls)………………………………………………………. 74 10.1 Opbouw…………………………………………………………………….. 75 10.2 Instellingen………………………………………………………………….76 Klasse Uitvoer (Uitvoer.cls)……………………………………………………….. 76 11.1 Opbouw…………………………………………………………………….. 77 11.2 Instellingen………………………………………………………………….78 Hoofdprogramma (Magazijnsimulator.frm)……………………………………….. 79 12.1 Opbouw…………………………………………………………………….. 79 12.2 Instellingen………………………………………………………………….84 Output……………………………………………………………………………….84 13.1 tijd………………………………………………………………………….. 84 13.2 rekken……………………………………………………………………….85 13.3 routes……………………………………………………………………….. 86
Hoofdstuk 5: Routeringsalgoritme 1 Inleiding……………………………………………………………………………. 87 2 Het kortste pad algoritme van Dijkstra…………………………………………….. 88 2.1 Algemeen…………………………………………………………………... 88 2.2 Algoritme…………………………………………………………………... 88 3 Implementatie routeringsalgoritme in magazijnsimulator…………………………. 91 3.1 Inleiding……………………………………………………………………. 91 3.2 Controle of Dijkstra’s algoritme van toepassing is………………………… 93 3.2.1 Controle of start- en doellocatie in dezelfde enkele pickgang liggen 93 3.2.2 Controle of start- en doellocatie in dezelfde dubbele pickgang liggen………………………………………………………………..96 3.3 Dijkstra’s algoritme niet van toepassing…………………………………… 96 3.4 Dijkstra’s algoritme wel van toepassing…………………………………… 97 Inhoudstafel
iii
Hoofdstuk 6: Simulaties 1 Inleiding……………………………………………………………………………. 104 2 Parameters in invoerbestand……………………………………………………….. 104 2.1 Layoutparameters…………………………………………………………...104 2.2 Snelheidsparameter………………………………………………………… 105 2.3 Picktijdparameter…………………………………………………………... 105 2.4 Constanten…………………………………………………………………. 106 2.5 Netwerk…………………………………………………………………….. 106 3 Uitvoeren van een simulaties………………………………………………………. 106 4 Bespreking simulaties……………………………………………………………… 113 Appendix A: Simulaties……………………………………………………………………. 114 Magazijnlayout 1…………………………………………………………………………... 115 Magazijnlayout 2…………………………………………………………………………... 116 Magazijnlayout 3…………………………………………………………………………... 117 Magazijnlayout 4…………………………………………………………………………... 118 Magazijnlayout 5…………………………………………………………………………... 119 Input Magazijn 2…………………………………………………………………………… 120 Output Magazijn 2…………………………………………………………………………. 127 Appendix B: Code…………………………………………………………………………. 132 1 Klasse Invoer………………………………………………………………………. 133 2 Klasse Magazijn……………………………………………………………………. 141 3 Klasse Order..……………………………………………………………………….150 4 Klasse Route.………………………………………………………………………. 153 5 Klasse Knoop………………………………………………………………………. 159 6 Klasse Dijkstra……………………………………………………………………... 161 7 Klasse Uitvoer………………………………………………………………………164 8 Formulier Intro……………………………………………………………………... 173 9 Formulier Magazijnsimulator……………………………………………………… 175 Bibliografische referenties…………………………………………………………………. 207
Inhoudstafel
iv
Hoofdstuk 1 :
Magazijnen en het belang van simulatie
1. Inleiding In het verleden is regelmatig met de komst van concepten zoals Just-In-Time, directe distributie en cross-docking de ondergang van distributiecentra en magazijnen voorspeld. Velen hebben beweerd dat het magazijn een ‘dinosaurus op sterven na dood’ is. Ook werd gedacht dat de voortschrijdende onwikkelingen op het gebied van informatietechnologie een enorme reductie in voorraden zou mogelijk maken, omdat veiligheidsvoorraden zouden kunnen worden gesubstitueerd voor informatie.
In werkelijkheid zou echter wel eens het tegenovergestelde kunnen gebeuren. Jenkins en Speh (1995) gebruiken de analogie met een vliegtuig om de toekomst van het distributiecentrum en het magazijn aan te duiden. Het doel van vliegtuigen, het overwinnen van zwaartekracht en het overbruggen van tijd en ruimte, is de afgelopen vijftig jaar nauwelijks veranderd, terwijl de constructie en structuur wel drastisch is aangepast. Daarbij zijn de prestaties en de veiligheid van vliegtuigen enorm verbeterd. Ook in de toekomst zal het vliegtuig deze rol blijven vervullen. Volgens Jenkins en Speh geldt hetzelfde voor het magazijn of ditributiecentrum.
Door de toenemende internationalisering van de handel, die mogelijk is gemaakt door verbeterde en versnelde communicatie- en transportmogelijkheden, zijn een groot aantal ontwikkelingen in gang gezet die ook de logistiek van de ondernemingen in sterke mate beïnvloeden. Men spreekt over globalisatie, meer bepaald globalisatie van de aanvoer, de productie en de distributie.
Logistieke ketens worden langer, de besturing ervan complexer en de afnemende partijen (klanten of groepen van klanten) weten hun wensen en eisen steeds duidelijker te formuleren naar de leveranciers toe. Dit gebeurt meestal in termen van betrouwbaarheid , snelheid en beschikbaarheid. Daarnaast gebeurt dit ook in termen
Hoofdstuk 1 : Magazijnen en het belang van simulatie
1
van
gewenste
‘customization’
en
het
stroomopwaarts
duwen
van
planningsveranderingen. Deze beweging is eigenlijk tegengesteld aan die van de producenten of leveranciers die voornamelijk gericht is op concentratie. In dit krachtenveld komt dan vooral de logistieke dienstverlening naar voren. Deze biedt nieuwe diensten aan : industriële value added services en value added logistics.
Tegenwoordig krijgt het warehouse soms de functie om bij te dragen aan productieoptimalisatie. Het takenpakket van een warehouse is dus dynamischer geworden. Bij dit alles wordt de term ‘warehousing’ steeds belangrijker. Onder warehousing verstaat men een brede familie waartoe alle activiteiten behoren die zich in een magazijn of distributiecentrum afspelen alsook alle aspecten die kunnen bijdragen tot het verbeteren, efficiënter maken of optimaliseren van deze activiteiten. Ondernemingen moeten zich steeds meer vragen stellen zoals : •
Bevindt ons magazijn of ditributiecentrum zich nog op de juiste locatie ?
•
Moeten we ons magazijn of distributiecentrum uitbreiden of inkrimpen ?
•
Passen inrichting en besturing nog bij de veranderende assortimenten en klantenwensen ?
•
Gebruiken we nog de juiste hulpmiddelen ?
De bestaande functies van een traditioneel distributiecentrum blijven ook in de toekomst bestaan, al is het wel zo dat sommige functies ten opzichte van vandaag aan belang zullen winnen. Hoewel door de opkomst van ketenintegratie voorraden tot op zekere hoogte kunnen worden gereduceerd, is het idee van een logistieke keten zonder voorraden voorlopig nog een utopie. Er zijn nog zeer veel producten die opslag behoeven door seizoenspatronen in de productie of de consumptie of als gevolg van het blijven bestaan van doorlooptijden en optimale en efficiënte seriegroottes.
Conclusies : •
De functie van het traditionele distributiecentrum wordt nog uitgebreid omdat door de mondialisering van de handel en de productie steeds grotere goederenstromen over de wereld worden verstuurd, tevens moeten nieuwe diensten en activiteiten in het distributiecentrum worden uitgevoerd.
Hoofdstuk 1 : Magazijnen en het belang van simulatie
2
•
Het distributiecentrum blijft zijn functie in de keten vervullen, maar nieuwe technologieën stellen het distributiecentrum in staat deze functie sneller, efficiënter en goedkoper te vervullen.
2. Efficiëntie van het orderverzamelen
Een van de belangrijkste activiteiten binnen een magazijn of distributiecentrum is het verzamelen van de producten op basis van een klantenorder, wat men het orderverzamelen noemt. De efficiëntie waarmee deze activiteit wordt uitgevoerd wordt bepaald aan de hand van de cyclustijd of doorlooptijd. Een lage doorlooptijd betekent dan automatisch een hoge customer service.
De doorlooptijd wordt voornamelijk beïnvloed door de inrichting van het magazijn en de besturing van de activiteiten binnen het magazijn. Ten aanzien van inrichting en besturing spelen eigenlijk twee totaal verschillende ontwikkelingen. Enerzijds zijn er de externe (markt)ontwikkelingen in assortimenten en klantenorderprofielen die om een verandering van inrichting, besturing en werkorganisatie vragen. Anderzijds zijn er de snelle technologische ontwikkelingen op het vlak van material handling-apparatuur en op het gebied van de informatieverwerking die het mogelijk maken om, door een andere inrichting, besturing en werkorganisatie, beter te presteren in termen van customer service en kosten (technology push). Door dergelijk ontwikkelingen wordt de economische levensduur van het bestaande systeem uiteraard verkort.
3. Simulatie
De beste combinatie van layout van het magazijn, het gebruikte materieel, de manier van opslaan en toewijzen van opslaglocaties kan men het best bepalen door het uitvoeren van simulaties. Daarbij worden via de computer proefopstellingen van het te beschrijven systeem nagebootst. Dit gebeurt aan de hand van kennis en/of veronderstellingen omtrent het gedrag van delen van het systeem en hun interactie. Het uiteindelijk doel is inzicht te verkrijgen in het gedrag van het systeem als geheel. Hoofdstuk 1 : Magazijnen en het belang van simulatie
3
Simulaties laten toe om op korte tijd experimenten uit te voeren en deze experimenten meerdere malen uit te voeren (eventueel met gewijzigde parameters). Nadelen zijn dat het bouwen van een goed model tijdrovend is en een goede kennis van het systeem vergt, dat simulatie steeds een benadering van een complexe reële situatie blijft waarbij de mate van benadering per definitie onmeetbaar is en dat simulatie niet automatisch leidt naar een optimale oplossing.
Tegenwoordig wordt simulatie steeds vaker ingezet als hulpmiddel voor ontwerp en systeemgedrag van magazijnproblemen. Daarvoor zijn een aantal redenen aan te halen : •
Binnen een magazijnomgeving is het interessant om het totale material handling systeem te analyseren. Simulatie is daarbij vooral aan te bevelen als de interacties tussen de deelsystemen relevant zijn.
•
Een magazijnomgeving is aan veel invloedsfactoren onderhevig wat het gebruik van mathematische modellering en oplossingsprocedures moeilijk maakt.
•
De grote progressie in computerhardware en simulatiesoftware.
Simulatie geniet dus de voorkeur op het gebuik van een aantal standaardformules omdat deze laatste slechts in een beperkt aantal gevallen bruikbaar zijn. Simulatie laat ook toe om grote aantallen alternatieven met elkaar te vergelijken. Zo wordt in de ontwerpfase, maar ook op een later tijdstip bij de heroverweging van methoden, vaak een compleet simulatiemodel gemaakt van het magazijn, waarmee verschillende besturingsmethoden worden getest.
Hoofdstuk 1 : Magazijnen en het belang van simulatie
4
Hoofdstuk 2 :
Organisatie van magazijnen en order picking
1. Magazijnen en distributiecentra
In een magazijn wordt de voorraad van een distributieketen aangehouden. Wanneer dit magazijn een groot deel van de markt bedient spreken we van een distributiecentrum (DC).
1.1
Functies van een distributiecentrum
1) Voorraadfunctie of opslag Het gedurende een bepaalde tijd bijhouden van goederen, teneinde de inkomende en de uitgaande materiaalstromen te ontkoppelen, dus het fungeren als buffer tussen productie en consumptie en het consolideren van goederenstromen.
2) Groupagefunctie Indien goederen die samen verstuurd moeten worden uit verschillende locaties worden aangevoerd, zal het DC de goederen tijdelijk bijhouden tot alle nodige goederen (hetzij qua mix hetzij qua volume) bijeen gebracht zijn.
3) Sorteerfunctie of bulk breaking functie Wanneer de aangevoerde eenheidsvolumes te groot zijn om als zodanig te worden verstuurd. Meestal zal het DC instaan voor het uitpakken van grote hoeveelheden en het verzamelen van individuele klantenorders (het order picken).
4) Overslagfunctie Vaak dient men alleen maar te veranderen van vervoermiddel, bijvoorbeeld van binnenvaartschip naar trein of truck.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
5
5) Customer Service Het creëren van een assortiment voor productieprocessen en de handel, het verbeteren van costumer service (wat alleen bereikt kan worden als aan de eisen van de klanten met betrekking tot het afleveren van de gevraagde producten kan worden voldaan) door het strategisch positioneren van producten in de logistieke keten
1.2
Functionaliteiten van het distributiecentrum
1.2.1
Functionele eisen aan distributiecentra
•
Flexibiliteit en reactiesnelheid Flexibiliteit en reactiesnelheid zijn noodzakelijke voorwaarden om efficiënt en effectief goederenstromen te kunnen verwerken. Een distributiecentrum moet op de middellange termijn veranderingen in het assortiment kunnen opvangen en moet ook plotselinge veranderingen in orderpatronen kunnen verwerken. De minder wordende merkentrouw en de veranderingen in het bestelgedrag van klanten versterken deze behoefte. Ten slotte zijn flexibiliteit en reactiesnelheid belangrijk vanuit het customer service-oogpunt. Klanten eisen in toenemende mate dat ze nog last minute-orders kunnen plaatsen of orders kunnen veranderen. Flexibiliteit op lange termijn heeft betrekking op het volume dat door een distributiecentrum moet kunnen worden verwerkt. Er moeten uitbreidingen gerealiseerd kunnen worden zonder dat de huidige activiteiten verstoord worden. In dat opzicht kunnen geografische veranderingen in de vraag minder goed worden opgevangen. Dit kan alleen verkregen worden door ‘flexibele’ contracten af te sluiten met logistiek dienstverleners.
•
Handling-capaciteit van het distributiecentrum Om onzekerheid, voorraden en verouderings- en afbreukrisico zoveel mogelijk te Reduceren of op te vangen, hebben ondernemingen de tactiek van kleine, maar frequente bestellingen ingevoerd. Dit betekent dat inkomende goederenstromen, de hoeveelheden die moeten worden opgeslagen en de uitgaande goederenstromen uit kleinere eenheden bestaan. Hierdoor stijgt het aantal orders en orderregels dat
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
6
moet verwerkt worden drastisch. Ook de centralisatie van de voorraden leidt tot een grotere behoefte aan handling-capaciteit, omdat daarbij het volume dat per distributiecentrum moet verwerkt worden, groter wordt. •
Doorlooptijd Het distributiecentrum moet ook bijdragen aan het verkorten van de doorlooptijden. Om aan deze eisen te kunnen voldoen moeten de orderverwerking, het orderpicken en het verzendgereed maken van de goederen in enkele uren voltooid zijn, omdat het grootste gedeelte van de levertijd in beslag genomen wordt door het transport van het distributiecentrum naar de klant.
•
Opslagcapaciteit Deze wordt bepaald door twee tegengestelde bewegingen. Enerzijds stijgt het aantal SKU’s (Stock Keeping Units) ten gevolge van de toename van het aantal productvariaties en de centralisatie van de voorraden, dit leidt op zijn beurt tot een toename van het aantal opslaglocaties en dus van de totale opslagcapaciteit. Anderzijds wordt de opslagruimte gereduceerd o.a. door het toepassen van informatietechnologie,
door
de
ketenintegratie
en
doorlooptijd-
verkortingstrategieën.
1.2.2
Value Added Logistics
Met de term VAL of Value Added Logistics wordt de waardetoevoegende activiteiten bedoeld. Deze activiteiten hebben twee karakteristieken die ze als waardetoevoegend kwalificeren. Eerst en vooral zijn het activiteiten die traditioneel niet in een magazijn worden uitgevoerd (wat wel het geval is voor activiteiten zoals goederenontvangst, opslag, order picken en verzenden) en ze zijn verschoven van de klant naar de leverancier of de logistieke dienstverlener. Labelen, om- en verpakken, testen van producten, repareren, kwaliteitscontrole en conditioneren zijn voorbeelden van activiteiten die als Value Added Logistics worden aangeduid.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
7
1.2.3
Assemblage-activiteiten
Producenten worden gedwongen het klantenorderontkoppelpunt stroomopwaarts te verschuiven, door de toename van het aantal productvariëteiten en het verkorten van de productlevenscycli. Daardoor komen de assemblage-activiteiten veelvuldig in de magazijnen en distributiecentra terecht omdat de productievestigingen meestal te ver van de markt verwijderd zijn om een korte levertijd te garanderen.
Voor het distributiecentrum houdt dit drie gevolgen in : 1) er worden componenten en onderdelen op voorraad gehouden in plaats van gereed product 2) er moet extra ruimte voorzien worden voor die activiteiten 3) er moeten binnen dezelfde levertijd meer activiteiten uitgevoerd worden, daardoor is er nood aan meer coördinatie en beheersing van de activiteiten
1.2.4
Locatie van het distributiecentrum
De centralisatie van de voorraden, het herontwerpen van de distributie-infrastructuur en de toename van het volume dat door de distributiecentra moet worden verwerkt leidt tot een kleiner aantal, maar grotere distributiecentra. De keuze van een geschikte locatie wordt daarbij alsmaar belangrijker.
1.3
De hoofdprocessen in het magazijn
1.3.1
Ontvangst van de goederen (inslag)
Met de ontvangst van de goederen bedoelt men de aanvoer en het uitladen van de goederen. Tijdens dit proces worden de goederen die binnenkomen getransporteerd tot aan de laadkaai, waar ze gelost worden met aangepaste hulpmiddelen en overgebracht worden naar een transitzone. In deze zone worden de goederen gecontroleerd op aantal, op volledigheid en op kwaliteit. De instructies daarvoor zijn vastgelegd in het werkhandboek wat deel uitmaakt van het kwaliteitshandboek en het kwaliteitsplan. Indien nodig zal men de goederen ook herverpakken, namelijk als de opslag van de Hoofdstuk 2: Organisatie van magazijnen en orderpicking
8
goederen een standaardverpakking vereist. De goederen worden geregistreerd op basis van hun artikelcode of inkoopordernummer en vaak ook gekoppeld aan een containerid. De ontvangstruimte of transitzone staat uiteraard ook in voor de verwerking van eventuele retourzendingen. Men spreekt ook van de dock-to-stock-procedure.
1.3.2
Verzenden naar de opslag
Dit proces wordt ook wel de dispatching genoemd. Deze dispatch-functie omvat de keuze van de opslaglocatie, het checken of de bedoelde opslagcapaciteit wel vrij is en een verplaatsing met intern transportmaterieel naar de opslagruimte gevolgd door een plaatsingshandeling (positioneren).
1.3.3
Opslag
Opslag houdt de fysieke opslag in van de goederen tot op het ogenblik dat de vraag zich voordoet. Dit proces gebeurt door de magazijnbedienden. De goederen worden ofwel onmiddellijk vanuit de transitzone opgeslagen, ofwel worden ze op een laadstation opgesteld, dan zal er eerst een gabarietcontrole plaatsvinden, daarna worden de goederen binnengenomen in het AS/RS. De registratie van de binnenkomende goederen linkt de computer-id aan de locatie-id. Dit registreren gebeurt meestal met scanners en barcodes, omdat een fout het gevaar met zich meebrengt dat men het product niet meer terug vindt in het magazijn.
1.3.4
Uitslag
Het uitslagproces omvat het uit de voorraad halen van de goederen. Dit gebeurt op basis van een verschepingsplan, waarbij men in de mate van het mogelijke poogt om gecombineerde ritten op de stellen. Men wil inslag- en uitslagbewegingen combineren tot een dubbele cyclus. De uitgeslagen goederen worden na het uit de voorraad halen ofwel in een pre-loading zone samengebracht of worden naar een orderverzamellocatie gebracht.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
9
1.3.5
Orderverzamelen
Orderverzamelen is het proces waarbij producten verzameld worden vanaf een specifieke opslaglocatie op basis van klantenorders. Welbepaalde verschillende artikelen worden uit een voorrraad verzameld en samengesteld tot een specifiek order. Onder deze definitie vallen zowel ‘externe orders’ als ‘interne productie-orders’. Over het algemeen is het orderverzamelproces of pickingproces het meest arbeidsintensieve en geautomatiseerde van alle processen en dus wellicht het belangrijkste proces binnen een magazijn. In sommige gevallen zijn zelfs 60% van alle processen terug te voeren tot het orderverzamelproces. Dit wordt nog versterkt door de evolutie van zogenaamde webwinkels (zoals Amazon.com). Deze heeft geleid naar steeds kleiner wordende bestellingen die kostenefficiënt moeten verzameld worden. Het verbeteren van de efficiëntie van het orderverzamelen betekent over het algemeen het minimaliseren van de verwachte orderverzameltijd.
1.3.6
Verpakking, laden en verzenden
Deze functie omvat onder andere de controle op de volledigheid van de afgeleverde orders, de verpakking per order, het groeperen van orders per bestemming en het genereren van allerhande documenten (zoals bijvoorbeeld transportnota’s en verzendingslabels). De verpakkingsactiviteit krijgt vanuit ecologisch standpunt tegenwoordig steeds meer aandacht. De verpakte goederen worden vanuit de transitzone in de transportmiddelen geladen. Dit kan bijvoorbeeld gebeuren met aangepaste laadzones en beweegbare laadkaaien, waarbij de heftruck rechtstreeks de vrachtwagen kan binnenrijden.
1.3.7
Informatieverwerking
De informatiestroom binnen een magazijn verdient minstens evenveel aandacht als de goederenstroom. Parallel met de goederenbehandeling en opslag moeten gegevens en informatie worden opgenomen. Het informatieproces kan ondersteund worden met diverse moderne technieken zoals Electronic Data Interchange (of EDI) (bij de plaatsing van orders), radiotransmissie (voor order picking) en barcoding (voor Hoofdstuk 2: Organisatie van magazijnen en orderpicking
10
automatische identificatie). Benetton gebruikt bijvoorbeeld sinds kort RF (radio frequent) ID tags in kledij in plaats van barcodes. De ID tag stuurt zijn coördinaten door naar het computergestuurde supply chain netwerk. Op die manier kan men gemakkelijk de status van de voorraad nagaan en de plaats van het kledingstuk traceren.
1.3.8
Cross-docking
We spreken van cross-docking indien men de processen ontvangst, picking/sorteren en verzenden combineert zonder tussenin de goederen op te slaan.
1.3.9
Overzicht
De hiervoor geschetste processen kunnen niet los van elkaar gezien worden. Zo kunnen bijvoorbeeld de processen van het wegzetten in de voorraad en het order picken in één cyclus worden gecombineerd. Onderstaande figuren geven een overzicht van de verschillende processen :
Inslagproces
Leverancier Transport Lossen Controle goederen compleet Controle kwaliteit Herverpakken/voorverpakken In opslag brengen
Figuur 2.1
Overzicht inslagproces (Bron: Van Goor et al, 1996)
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
11
In opslag brengen
Uitslagproces
Grijplocatie aanvullen Orderverzamelen Sorteren Verpakken Groeperen Gereedzetten Laden Transport
Afnemer
Figuur 2.2
1.4
Overzicht uitslagproces (Bron: Van Goor et al, 1996)
Soorten magazijnen
Eerst en vooral moet er een onderscheid gemaakt worden tussen een productiemagazijn en een distributiemagazijn : •
productiemagazijn De producten worden hier enkel tijdelijk in opgeslagen. We vinden ze vooral terug bij fabrieken. De geproduceerde goederen worden hier opgeslagen tot ze naar de
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
12
koper kunnen gestuurd worden. Een distributiemagazijn of handelsmagazijn zou zo’n koper kunnen zijn. •
distributiemagazijn Hier worden verschillende producten ontvangen van een externe leverancier. De producten worden eerst opgeslagen en vervolgens weer verkocht. Er wordt geen enkele waarde aan de producten toegevoegd, maar door het opslaan (mogelijk in kleinere hoeveelheden) en het hergroeperen van de producten kan de distributie goedkoper zijn dan een directe levering van leverancier naar de klant.
De meeste producten worden opgeslagen in één van de volgende magazijnsoorten : •
brede gangen palletmagazijn Dit is een magazijn waarvan de gangen een breedte hebben van 2,1 à 3,5 meter. Deze breedte wordt voornamelijk bepaald door het gebruikte transportmiddel, zo vereist een vorkheftruck een gang met een breedte die anderhalf keer zijn eigen lengte bedraagt. Ook de afmetingen van het gebruikte opslagsysteem speelt een rol. De items worden opgeslagen op pallets omdat de items zelf of de grote hoeveelheid van de items niet op legborden passen. Dikwijls is het zo dat vanuit de onderkant van de stellingen de items worden verzameld en de bovenste gedeelten worden gebruikt voor de bulkopslag. De orderverzamelaars gebruiken een bepaalde truck om de orders mee te verzamelen. Verschillende trucks zijn geschikt voor een dergelijk magazijn : contragewichttrucks, reachtrucks, pallettrucks en orderverzameltrucks.
•
smalle gangen palletmagazijn In dit magazijn hebben de gangen een breedte van ongeveer 1,5 à 1,8 meter. Analoog aan het voorgaande type spelen dezelfde factoren een rol bij het bepalen van de gangbreedte. Ook hier bevinden de producten zich weer op pallets om analoge redenen als bij een brede gangen magazijn. Dit type wordt voornamelijk gebruikt voor de opslag van redelijk grote, niet snellopende artikelen die alleen opgeslagen kunnen worden
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
13
op pallets. Het intern transportmaterieel kan bestaan uit orderverzamelkranen en smalle gangen hoogbouwtrucks. •
legbordmagazijn Bij dit magazijn varieert de breedte van de gangen tussen de 1,2 à 1,5 meter. De items worden hier opgeslagen op legborden in kleine hoeveelheden. Het orderverzamelen gebeurt doorgaans al lopend, eventueel mits het gebruik van een karretje.
Uiteraard is het mogelijk om een combinatie van deze categoriën te gebruiken, zodat in een gedeelte van het magazijn de producten opgeslagen zijn op pallets en in een ander gedeelte op legborden.
1.5
Layout van een magazijn
Algemeen is een magazijn opgebouwd uit pickgangen, eventueel aanvulgangen, middengangen (of dwarsgangen) en een depot. •
pickgangen Dit zijn gangen waarin de orderverzamelaars zich moeten begeven om de goederen of producten te kunnen picken. Soms dienen de pickgangen ook als aanvulgangen. In andere gevallen worden daarvoor aparte gangen voorzien. Dit is afhankelijk van het gebruikte opslagsysteem. Bij palletstellingen zijn pickgangen en aanvulgangen meestal gelijk. Bij doorrrolstellingen is dit niet het geval.
•
middengangen Dit zijn gangen die loodrecht staan op gangen waaruit verzameld wordt (pickgangen). Deze gangen bevatten zelf geen items en hebben als voornaamste functie het mogelijk maken om van gang te wisselen. Men spreekt soms ook van wisselgangen. Deze gangen hebben, in de situatie dat orders van meerdere locaties moeten worden verzameld, in het algemeen het voordeel dat de rijroutes korter
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
14
worden. Wisselgangen nemen echter ruimte in en kunnen er soms toe leiden dat de routes juist langer worden. •
depot locatie De depot locatie is een plaats in het magazijn waar alle in- en uitslagprocessen starten en eindigen. Het is het punt in het magazijn waarvandaan de verzamelaars hun pickrondes (of routes) doorheen het magazijn beginnen en eindigen. Het depot moet bezocht worden om een verzamelllijst te verkrijgen en om de verzamelde orders af te zetten. Het depot kan gesitueerd zijn aan de linkerkant, de rechterkant of in het midden van het magazijn. Meestal ligt het depot dichtbij het beginpunt, wat het depot zowel het begin- als het eindpunt van de orderverzamelroute maakt.
Als de verzamelaars alleen maar hun routes kunnen starten en eindigen op een punt, dan kan er gesproken worden van een centraal depot of een centraal afgiftepunt (CAP). Dit punt kan overal in het magazijn gesitueerd zijn, maar een gebruikelijke locatie is aan het begin of het einde van een gang. In het concept van de magazijnsimulator wordt uitgegaan van een dergelijk centraal afgiftepunt.
Daarnaast is het ook mogelijk dat er terminals in een magazijn gebruikt worden. In dit geval wordt er ook gebruikt gemaakt van een transportbaan en is een orderverzamelaar niet gebonden niet gebonden aan een centraal depot om zijn verzamelde orders af te zetten.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
15
Figuur 2.3
Magazijnlayout met centraal depot
2. Organisatie van de opslag
2.1
Opslagpolitieken
In principe bestaan er drie basisbenaderingen om de opslag van goederen in een magazijn te organiseren : •
Opslag naar vaste locatie (dedicated storage) : waarbij elk product een vaste locatie toegewezen krijgt (fixed locations)
•
Opslag naar vaste zones : deze verdeelt men meestal onder in hoge, middel en lage volumes, waarbij de hoge volume-zones het dichtst bij de laaddokken zijn gesitueerd en de lage volumes het verst, binnen deze zones kan men dan opslaan naar vaste of vrije locatie
•
Opslag naar vrije locatie of chaos magazijn (randomized storage): hierbij krijgt elk inkomend product (met behulp van een computer) een locatie toegewezen. De meest operationele definitie van vrije opslag is de ‘dichtstbijzijnde beschikbare locatie politiek’, bij binnenkomst in het magzijn wordt het product opgeslagen in
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
16
de dichtstbijzijnde geschikte magazijnlocatie en de producten verlaten dan het magazijn op FIFO-basis (First-in, First-out). Automatische systemen zijn steeds chaos georganiseerd.
Daarnaast bestaat er ook nog een hybride vormen. Een voorbeeld van een hybride vorm van opslagpolitiek is de splitsing in bulk- en pick-voorraad. Hierbij gebeurt het orderverzamelen vanuit vaste locaties, terwijl er voor de bulkvoorraad vrije opslag wordt toegepast.
Er bestaat dus een mogelijkheid om de opslagruimte op te splitsen in een bulkvoorraad enerzijds en een werkvoorraad (of pick-voorraad) anderzijds. De werkvoorraad, die goed bereikbaar is, dient voor het orderverzamelen, terwijl de bulkvoorraad op de achtergrond blijft. Dit leidt tot een versnelde orderpicking, die dan weer gecompenseerd wordt door de bijkomende handelingen die gepaard gaan met het aanvullen van de werkvoorraad vanuit de bulkvoorraad.
Voor een degelijke organisatie van de opslag is ook informatie nodig over het relatieve belang van de artikelen. Daarvoor kunnen we gebruik maken van de ABCanalyse. Hierbij bepalen we een Pareto-curve of ABC-curve op basis van de relatieve vraag van producten. De A-producten zijn dan de fast moving items (FMI) en de Cproducten zijn dan de slow moving items (SMI). In vele gevallen maken de Aproducten slechts 20% uit van het totale productgamma, maar zijn ze wel verantwoordelijk voor 80% van de omzet (20/80-regel). De A-producten worden dan ook logischerwijs het dichtst bij de laaddokken of het orderverzamelpunt geplaatst. De C-producten het verst.
2.2
(Eenheids)ladingsdragers
Om de in- en uitslag van goederen te vergemakkelijken worden ladingsdragers gebruikt. De meest voorkomende ladingsdrager is uiteraard het pallet. De ladingsdragers worden soms afgestemd op het product dat in opslag gaat. Maar de efficiëntie
wordt
sterk
verbeterd
door
het
gebruik
van
uniforme
eenheidsladingdragers. Hoofdstuk 2: Organisatie van magazijnen en orderpicking
17
Tabel 2.1
Standaard afmetingen van ladingsdragers (ISO/DIN)
Standaard
Bijzondere afmetingen 400 mm x 600 mm 600 mm x 800 mm
800 mm x 1000 mm 800 mm x 1200 mm (DIN 15145) 1000 mm x 1200 mm (ISO/DIN 55405) 1200 mm x 1600 mm 1200 mm x 1800 mm
2.3
Opslagsystemen
Het opslagsysteem vormt de kern van de meeste distributiecentra en magazijnen. Het doel en de functie van ieder opslagsysteem is steeds tweeledig. Er moet namelijk een onderscheid gemaakt worden naar : •
bufferfunctie : goederen worden ontvangen om verzonden te worden naar klanten, tussen het moment van ontvangen en de verzending bevindt zich een tijdsperiode, afhankelijk van de lengte van deze tijdsperiode gaan de goederen tijdelijk in opslag (tijdsoverbrugging)
•
orderverzamelfunctie : de opgeslagen goederen moeten verzameld worden voor de klant, de in de opslag aanwezige producten moeten dus op het juiste moment bereikbaar zijn en wel zodanig dat de orderverzamelkosten geminimaliseerd worden, daarnaast worden verschillende producten voor één klantenorder samengebracht
Bij de classificatie van opslagsystemen kunnen we een onderscheid maken tussen statische en dynamische opslagsystemen. Bij statische opslagsystemen gaan de goederen in niet-bewegende opslagsystemen. Voorbeelden zijn de traditionele palletstelling, in/doorrijstellingen, blokstapeling. Bij
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
18
dynamische opslagsystemen gaan de goederen ofwel in bewegende opslagsystemen in opslag, ofwel bewegen de goederen zelf in het opslagsysteem. Voorbeelden zijn paternosterstellingen, verrijdbare stellingen, doorrolstellingen. We geven hier een overzicht van de meest gebruikte opslagsystemen waarbij we vooral aandacht besteden aan de efficiëntie op het vlak van orderverzamelen.
2.3.1
Blokstapeling
Een eenvoudige opslagmethode waarbij de bulkgoederen zoveel mogelijk op mekaar worden gestapeld. Deze methode is zeer goedkoop maar heeft als nadelen dat FIFOuitslag (First-in, First-out) niet mogelijk is, dat er problemen onstaan als ook minder dan volle pallets moeten verzameld worden en dat er een verlies aan ruimtebenuttiging is.
Figuur 2.4 2.3.2
Blokstapeling
Palletstellingen
Palletstellingen zijn de meest voorkomende opslagsystemen door hun universele en flexibele toepassingsmogelijkheden en de realief goedkope investering. Ze worden gebruikt voor het opslaan en orderverzamelen van gepalletiseerde goederen en kunnen gemakkelijk omgebouwd en uitgebreid worden. Bovendien is elk pallet gemakkelijk bereikbaar in dit systeem. Dit systeem wordt vooral gebruikt voor magazijnen met grote hoeveelheden per artikel en voor gemiddelde tot hoge rotatie. Doorgaans worden de aanvul- en orderverzamelgangen gecombineerd. Hoofdstuk 2: Organisatie van magazijnen en orderpicking
We maken een 19
onderscheid tussen één-plaats-systemen en meer-plaats-systemen naargelang het aantal naast elkaar geplaatste pallets. Daarnaast kunnen we ook nog een onderscheid maken tussen langsstapeling en dwarsstapeling. De eerste methode wordt bij de opslag van de bulkvoorraad toegepast, de tweede voor het direct orderverzamelen. Met het oog op orderverzamelen zijn de voordelen dat ieder pallet in principe afzonderlijk bereikbaar is en dat een pallet met speciale afmetingen naast gewone pallets kan geplaatst worden. Nadeel is wel de slechte ruimtebenuttiging omdat er veel ruimte nodig is voor de gangpaden.
Figuur 2.5 2.3.3
Palletstellingen
Legbordstellingen
Legbordstellingen worden vooral gebruikt voor de opslag van kleinere producten. De bulkvoorraad is tevens de grijpvoorraad of werkvoorraad. Ze worden bij voorkeur gebruikt wanneer per artikel slechts kleine tot middelgrote hoeveelheden uit een zeer breed assortiment herbevoorraad worden of voor orderverzameling beschikbaar gesteld worden. Voordelen op het vlak van orderpicking zijn overzichtelijkheid en controle van de inventaris en de mogelijkheid tot automatisering. Bovendien kan de orderverzamelcapaciteit eenvoudig aangepast worden aan de op dat moment
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
20
benodigde capaciteit. Door een goede organisatie van de aan- en afvoer van goederen, kan deze capaciteit worden uitgebreid door meer mensen in te zetten.
Figuur 2.6 2.3.4
Legbordstellingen
Doorrolstellingen
Doorrolstellingen worden zowel voor pallets als voor dozen (of colli) gebruikt. De pallets of dozen worden hierbij achter elkaar geplaatst in een opslagkanaal met rollenbed. Door het rollenbed lopen ze in het kanaal automatisch van de inbreng- naar de orderverzamelzijde. Dit leidt tot een compacte opslag met hoge vullingsgraad. Doorrolstellingen worden voornamelijk toegepast voor snelroterende artikelen en in situaties waarbij de bulkvoorrraad en de orderverzamelvoorraad gesplitst zijn. Het FIFO-systeem is volledig toepasbaar. De orderverzameling gebeurt over het algemeen door orderverzamelaars die langs de pick-locaties lopen en de goederen verzamelen.
De belangrijkste voordelen
van
doorrolstellingen
met
betrekking
tot
het
orderverzamelen zijn : •
Er kan tot 40% à 70% bespaard worden op de doorlooptijd.
•
De ruimtebesparing kan tot 30% oplopen.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
21
•
Minder fouten bij het orderverzamelen, dit omdat de concentratie tijdens het orderverzamelen verhoogt door een compacte en overzichtelijke schikking van de producten.
•
Minder tijdverlies door de vergemakkelijking van de taakverdeling en de verbetering van het overzicht veroorzaakt door lange en rechte gangen.
•
Hogere productiviteit, de scheiding van
de bevoorradingszone en de
orderverzamelzone voorkomt immers opstroppingen in druk bezette gangen, daardoor verhoogt de individuele productiviteit van de werknemers.
Figuur 2.7 2.3.5
Doorrolstelling
Paternosteropslag
Een paternostersysteem, of met andere woorden een geautomatiseerd verticaal opslagsysteem, laat compacte opslag toe. Deze manier van opslaan resulteert in een enorme ruimtebesparing aangezien men de hoogte van magazijn maximaal kan benutten. Het wordt vooral gebruikt voor kleine stukgoederen (bijvoorbeeld : electronica, snijgereedschappen). De order picking gebeurt volgens het goederennaar-man principe. De voordelen op het vlak van orderverzamelen zijn de tijdswinst, de verbetering van de werkomstandigheden en de foutreductie.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
22
Figuur 2.8 2.3.6
Principe paternosteropslag
Draagarmstellingen
Bij dit type stelling zijn enkel aan de achterzijde staanders voorzien waaraan draagarmen worden bevestigd. De stelling is hierdoor aan de voorzijde vrij toegankelijk. Er kunnen lange materialen in opgeslagen worden. De zware constructie die nodig is om de draagarmen met last te ondersteunen is relatief duur. Het orderpicken verloopt gelijkaardig als het picken bij een palletstelling.
2.3.7
Insteekstellingen
Dit type stelling is eveneens bestemd voor de opslag van lange materialen. De stelling is opgebouwd uit kanalen die naast en boven elkaar liggen. Het grote voordeel daarvan is dat er veel grijplocaties op een beperkt stellingoppervlak ontstaan. Dit heeft voor gevolg dat de orderverzameltrajecten kort zijn. Het nadeel is dan weer dat aan de kop van de stelling veel ruimte benodigd is om de goederen uit de stelling te halen wat aanleiding geeft tot brede gangen.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
23
2.3.8
AS/RS hoogtemagazijn
Een Automatic Storage/Automatic Retrieval magazijn kan de meest economische oplossing
zijn
bij
zeer
hoge
frequenties
van
in-
en
uitslag.
De
stellingsbedieningsapparatuur (SBA) bestaat voornamelijk uit kranen. Daarbij onderscheiden we bemande en onbemande vormen alsook manueel of automatisch bestuurde vormen. Meestal zal men zeer hoog moeten bouwen (d.w.z. 18 tot 30m) om een dergelijk systeem te implementeren om per kraan voldoende opslagcapaciteit te kunnen bedienen. De belangrijkste voordelen op het vlak van order picken is de bijna foutvrije picking van eenheidsladingen en de enorme tijdsbesparing zowel op de informatietijd, op de grijptijd als op de looptijd.
Figuur 2.9
Man-on-board AS/RS
Een miniloadsysteem is een meer compacte versie van een AS/RS systeem. Dit wordt gebruikt voor de behandeling van uniforme containers met beperkte omvang en
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
24
gewicht. Het kan zowel als opslagmiddel als als intern transportmiddel fungeren. In vergelijking met het volledige AS/RS systeem is het goedkoper en heeft het een hogere snelheid.
3. Het intern transportmaterieel
Hier
geven
we
een
overzicht
van
de
belangrijkste
infrastructuur
voor
materiaalbehandeling die in een magazijn aanwezig kan zijn.
3.1
Soorten transportmiddelen
3.1.1
Vorkheftruck of contragewichttruck
De vorkheftruck of contragewichttruck vormt een flexibel transport- en hefwerktuig. Deze truck wordt veel gebruikt bij de in- en uitslag van pallets in magazijnen. Hij koppelt snelheid en hefkracht aan eenvoud van gebruik Er bestaat een groot aantal varianten op basis van aandrijving (diesel, gas of elektriciteit), maar ook op basis van de verschillende opzetstukken voor het behandelen van speciale goederen. Omdat vorkheftrucks vrijdragend zijn (d.w.z. dat ze hun lading buiten het voertuig dragen), kunnen deze trucks een grote variëteit aan lasten vervoeren, gaande van kleine pallets over bakken en rollen tot zelfs grote containers. De truck wordt in balans gehouden door een contragewicht. Er bestaan drie- en vierwieluitvoeringen. De belangrijkste voordelen zijn de grotere snelheid t.o.v. het andere intern transportmaterieel, het vermogen om zwaardere lasten te heffen en de minder hoge eisen die aan de ondergrond gesteld worden. De belangrijkste nadelen zijn de nood aan voldoende brede gangen om goederen in en uit te kunnen rijden (de "recul") en de geringe zijdelingse stabiliteit in de bochten, vooral dan met hooggeheven lading.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
25
Figuur 2.10 3.1.2
Vorkheftruck
Reachtruck of smalle gangen truck
De reachtruck of smalle gangen truck is in feite een smallere versie van de vorkheftruck. Het plaatsen en het uitnemen gebeurt door telescopische vorken. Dit heeft voor gevolg dat de truck niet achteruit moet rijden, waardoor de truck geschikt is voor smallere gangen. Het voordeel is de grote winst aan vloeroppervlakte. De nadelen zijn de lagere snelheid, de minder grote stabiliteit, en de hogere aankoopprijs t.o.v. een vorkheftruck.
Figuur 2.11
Reachtruck
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
26
3.1.3
Pallettruck
De meest voorkomende en meest simpele vorm van een pallettruck is de handpallettruck. Deze heeft veel toepassingen in het transport over korte afstanden, ondere andere bij het laden en lossen van vrachtwagens. Daarnaast bestaan er nog vele andere soorten niet-heffende pallettrucks.
Figuur 2.12 3.1.4
Handpallettruck
Stapelaar
Een stapelaar is universeel inzetbaar, heeft beperkte afmetingen en wordt onder andere gebruikt voor het laden en lossen van palletladingen met een klein of middelmatig volume. Daardoor is hij vaak een goedkoper alternatief voor de vorkheftruck. De bestuurder loopt met de stapelaar mee of bevindt zich rechtstaand of zittend op de stapelaar. Het heffen kan ofwel handmatig gebeuren ofwel met behulp van een elektromotor. De stapelaar draagt de last binnen de wielbasis van het voertuig, doordat de vork die de last draagt tussen voor- en achterwielen geplaatst is, en heeft daarom geen contragewicht nodig. Dit heeft voor gevolg dat de stapelaar een klein en stabiel voertuig is, geschikt voor magazijnen met smalle gangen.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
27
Figuur 2.13 3.1.5
Stapelaar
Automatic Guided Vehicle (AGV)
AGV’s zijn zeer geschikt voor frequent transport. Daarbij compenseren ze hun relatief gematigde snelheid door hun continue beweging. Ze kunnen gestuurd worden via computers, via draadloze communicatie (radio frequent of infrarood) of via inductiekabels in de vloer. Tegenwoordig worden ze steeds compacter en autonomer doordat ze on-board een layout van het magazijn kunnen hebben, waardoor ze zelf hun weg kunnen zoeken in het magazijn. Een andere actuele ontwikkeling is de combinatie van AGV’s en industriële robotten.
Figuur 2.13
Automatic Guided Vehicle
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
28
3.2
Overzicht
Voor de magazijnsimulator zijn vooral de rijsnelheden van de verschillende transportmiddelen belangrijk, alsook de hef- en daalsnelheden. Deze beïnvloeden immers de transporttijd en ook de picktijd eens men op de picklocatie is aangekomen. Daarom geven we hier een overzicht van de snelheden van de verschillende transportmiddelen.
Tabel 2.2
Snelheden van het intern transportmaterieel (Bron: De Koster et al, 1995)
Transportmiddel
Max rijsnelheid
Max hefsnelheid
Max daalsnelheid
(m/s)
(m/s)
(m/s)
(a)
(b)
(a)
(b)
(a)
(b)
Vorkheftruck
ca. 3,0
ca. 3,4
ca. 0,4
ca. 0,4
ca. 0,4
ca. 0,4
Reachtruck
ca. 2,5
ca. 2,8
ca. 0,4
ca. 0,4
ca. 0,4
ca. 0,4
Pallettruck
ca. 2,1
ca. 2,6
n.v.t.
n.v .t.
n.v.t.
n.v.t.
Handpallettruck
ca. 1,0
ca. 1,1
n.v.t.
n.v .t.
n.v.t.
n.v.t.
Stapelaar
ca. 1,2
ca. 1,5
ca. 0,3
ca. 0,1
ca. 0,3
ca. 0,1
AGV
ca. 2,0
ca. 2,0
n.v.t.
n.v.t.
n.v.t.
n.v.t.
(a) met lading (b) zonder lading n.v.t. = niet van toepassing (Bron: De Koster et al, 1995)
4. Order picking
Het orderverzamelproces werd al algemeen belicht in punt 1.3.5. In deze paragraaf gaan we dieper in op de karakteristieken van dit proces.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
29
4.1
Het orderverzamelproces
Het orderverzamelproces kan opgesplitst worden in een aantal deelprocessen : •
Het opzoeken van een picking-locatie
•
Het verplaatsen naar de locatie
•
Het grijpen van de goederen
•
Het registreren van de transactie
•
Het sorteren per order
•
Het verpakken
Deze deelprocessen kunnen ondergebracht worden in drie functies : •
Informatie (10 à 20 % van het tijdsaandeel) Het rendement van deze functie kan aanzienlijk verhoogd worden door logische, korte en eenduidige informatie te voorzien, zowel wat betreft aanduiding van de artikellocatie als wat betreft de vormgeving van de bijhorende documenten.
•
Beweging (50 à 70 % van het tijdsaandeel) De orderverzamelproductiviteit kan verbeterd worden door toepassing van ABCanalyse
bij
de
bepaling
van
de
magazijnlocaties,
de
invoering
van
gedecentraliseerde afgiftepunten voor afgewerkte orders en een geschikte strategie bij het bepalen van de orderverzamelroute. •
Grijpen (20 à 30 % van het tijdsaandeel) De productiviteit van deze functie kan aanzienlijk verhoogd worden door een ergonomische vormgeving van de stellingen, organisatorische verbeteringen bij de locatie van de voorraad in het magazijn en aandacht voor de uitnamefrequenties bij de bepaling van de magazijnlocaties.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
30
4.2
Orderverzamelstrategieën
4.2.1
Eéntraps - of ordergeöriënteerde verzamelmethode
De orderverzamelaar verzamelt per order de verschillende hoeveelheden artikelen, daarna geeft hij het volledige order aan de verpakkings- of verzendingsdienst en begint hij aan het volgende order. De items worden meegenomen in de volgorde waarin de orderverzamelaar de rekken aandoet. De orders worden dus sequentieel (na elkaar) afgehandeld. Het voordeel van deze methode is dat er geen nasortering meer nodig is.
Er kunnen ook twee of meerdere orders gelijktijdig verzameld worden. De artikelen worden dan tijdens het pickingproces al in afzonderlijke verzamelbakken per order gesorteerd. Dit reduceert de doorlooptijd.
Een variant van de ééntrapsmethode is het zonesysteem of het verzamelen in meerdere magazijnzones. In dit geval verzamelen meerdere pickers het order en worden de verzamelde (deel)orders van de ene naar de andere verzamelaar doorgegeven. We noemen dit de ‘pick and pass’ methode. Bij dit systeem kunnen de doorlooptijden aanzienlijk ingekort worden door het gebruik van aangepaste transportmiddelen en door de goede kennis van het deelassortiment van de pickers.
We kunnen ook parallel werken, dit betekent dat op verschillende plaatsen in de opslagruimte aan hetzelfde order wordt gewerkt. Op die manier kunnen grote orders versneld afgehandeld worden.
4.2.2
Meertraps - of artikelgeöriënteerde verzamelmethode
Deze methode wordt toegepast in magazijnen waar vele kleine orders met weinig verschillende artikels uit een omvangrijk assortiment moeten verzameld worden. In dit geval worden de binnenkomende orders
gescheiden in afzonderlijke
verzamelopdrachten per artikelsoort, zodat voor de verschillende orders de route maar één keer wordt afgelegd. In een eerste stap verzamelt de picker het totale aantal items
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
31
van alle orders samen. In een tweede stap worden de verzamelde artikels ordergeoriënteerd gesorteerd.
Ook hier kunnen we met een zonesysteem werken of parallel werken. Deze laatste vergt een grote organisatorische inspanning, maar garandeert een snelle respons in het geval van talrijke orders.
Pick-to-belt is een speciale vorm waarbij de pickers over een beperkt zone goederen picken naar een band, waarbij de goederen op het einde van de band worden verzameld tot orders.
4.2.3
Wave-picking (Pick, sort en pack)
De pick-batches worden vrijgegegeven in zogenaamde pick-waves. Een wave is een groep van orders die vaak met hetzelfde transport moeten worden vervoerd of die om een andere reden gelijktijdig vrijgegeven worden. Tijdens de verzamelrondgang worden alle artikels verzameld nodig voor de verschillende orders die tot de wave behoren. Deze methode gebruiken we in de magazijnsimulator (zie hoofdstuk 5)
4.3 •
Principes van orderverzamelen
Man-naar-goederen (pickers-to-parts) De picker beweegt door het magazijn en neemt tijdens zijn ronde de gewenste items uit de rekken. Hierbij kan hij al dan niet gebruik maken van één of ander type karretje of order-pickertruck. Deze vorm van picken doet zich voor bij statische opslagsystemen.
•
Goederen-naar-man (parts-to-pickers)
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
32
Hier bewegen de te picken items naar de picker of orderverzamelaar toe. Deze vorm is vooral geschikt voor hogere volumes en grotere orders en doet zich voor bij dynamische opslagsystemen. •
Automatische orderverzameling Hiervoor zijn bijvoorbeeld schachtautomaten (A-frames) en robots met grijparmen beschikbaar, maar de toepassing daarvan is nog marginaal. Nochtans zijn met deze systemen zeer hoge pickvolumes te bereiken.
4.4
Componenten van de orderverzameltijd of picktijd
De orderverzameltijd of picktijd bestaat uit vier componenten :
1) De reistijd als gevolg van het verzamelen van een order,
deze
correspondeert met de lengte van een pickronde die begint en eindigt bij het depot of het centraal afgiftepunt. De reistijd kan gezien worden als de som van de reistijd ‘binnen de gangen’, d.w.z de tijd die besteed wordt aan het reizen binnen de bevoorrade gangen en de reistijd ‘buiten de gangen’, d.w.z de tijd die besteed wordt aan het reizen tussen de gangen, dus om van de ene gang naar de andere te gaan.
2) De verwerkingstijd op de locatiepunten, d.w.z de tijd die nodig is om taken uit te voeren zoals het zoeken naar picklocaties het pakken van de items en het documenteren van de verzamelactiviteiten.
3) De administratietijd als gevolg van het starten en beëindigen van de verzamelroute, d.w.z de tijd nodig voor administratieve en opstarttaken, zoals bijvoorbeeld het ophalen of afzetten van verzamelmateriaal (karretje, rolcontainer, etc.), het verkrijgen van de orderverzamellijst.
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
33
4) De wachttijd tussen de verschillende opdrachten. Als de ene opdracht afgerond is en de volgende nog niet kan aangevangen worden, dan zal de picker of verzamelaar even moeten wachten.
Het verbeteren van de orderverzamelefficiëntie betekent over het algemeen het minimaliseren van de verwachte orderverzameltijd. Dit kan bijvoorbeeld gebeuren door het minimaliseren van de weglengte die de orderverzamelaar moet afleggen langs de rekken van het magazijn om een klantenorder samen te stellen. Daarvoor kunnen we terugvallen op diverse rekenalgoritmen die in het operationeel onderzoek werden ontwikkeld om kortste weg-problemen op te lossen. We hebben bij de opbouw van de magazijnsimulator geopteerd voor het kortste pad algoritme van Dijkstra. We gaan dieper in op dit algoritme in hoofdstuk 5. Een andere mogelijkheid zou bijvoorbeeld kunnen zijn om de informatieverwerking ter plaatse sneller te laten verlopen. Daarvoor bestaan aangepaste technieken.
4.5 •
Technieken voor efficiëntere informatieverwerking bij het picken
Pick-to-light Hier wordt op elke locatie in het rek een licht voorzien. Voor elke pickopdracht stuurt de computer het te picken artikel en het aantal naar een scherm en branden de lichtjes op de te picken locaties. Het kunnen soms lichtknoppen zijn waarop de picker moet drukken telkens hij of zij een item pickt. Dit is dan om fouten te vermijden.
•
Pick-to-display Analoog als bij pick-to-light, maar nu is naast het verklikkerlampje ook een LEDdisplay voorzien waarop het aantal wordt getoond. Het geheel wordt aangestuurd door een centrale computer.
•
Paperless picken Dit is een systeem waarbij men de orderverzamelaars uitrust met draadloze koptelefoons, waarbij een computerstem dan te picken artikelen voorleest. De
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
34
terugmelding gebeurt met een draadloze barcode scanner. Dit kan ook gecombineerd worden met één van de vorige technieken. Een verduidelijking van het verschil in efficiëntie tussen picken met papier en papierloos picken geven we in onderstaande tabel.
Tabel 2.3
Verschil tussen papier en papierloos picken Papier (s)
Stellingdisplay (s)
Lezen van het locatienummer
0,3
-
Artikelnummer lezen of display herkennen
1,8
0,3
Blik afwenden van papier
0,5
-
Artikelnummer op locatie lezen
1,0
-
Blik afwenden
0,5
-
Artikelaantal lezen
0,3
0,3
Artikelaantal bevestigen
2,0
0,3
Opnemen en wegleggen papier
1,0
-
Totaal
7,4
0,9
Hoofdstuk 2: Organisatie van magazijnen en orderpicking
35
Hoofdstuk 3 :
Gebruik van Visual Basic 6.0 en Microsoft Excel
1. Inleiding
Als ontwikkelomgeving voor de magazijnsimulator is geopteerd voor Visual Basic 6.0. Deze keuze is vooral gebaseerd op het feit dat Visual Basic en goede interactie met Microsoft Excel mogelijk maakt. De invoer van de magazijnlayout, de orderspecificaties, het knopennetwerk (zie 2.1.3 Routering, hoofdstuk 4) alsook het gebruikte intern transportmaterieel en de gebruikte opslagsystemen gebeurt immers via een Excel-file. Tevens gebeurt de uitvoer van de doorlooptijden, transporttijden, afstanden, rekinhouden en routes via een Excel-file.
Als we Visual Basic starten, dan starten we tegelijkertijd de geïntegreerde ontwikkelomgeving IDE (Integrated Development Environment). Een programma dat binnen deze omgeving ontwikkeld wordt noemen we een VBAprogramma. VBA is immers de programmeertaal van Visual Basic. Conceptueel kunnen we de relatie tussen VBA en Visual Basic als volgt weergeven :
Visual Basic = VBA + ontwikkelomgeving + compiler
De ontwikkelomgeving is het programma waarin VBA-programma’s worden opgeslagen, uitgevoerd en getest. De taak van de compiler bestaat erin om een programmatekst (programmacode), of met andere woorden de door ons ingevoerde instructies te vertalen in een uitvoerbaar programma. Deze compiler kunnen we op elk ogenblik starten. Staan op dat ogenblik nog compile time fouten in het programma dan worden deze weergegeven. Als we de compiler oproepen verschijnt er een dialoogvenster waarin we de naam van het EXEbestand invoeren of de standaardnaam overnemen. Er wordt dan een EXE-bestand gemaakt dat we altijd kunnen uitvoeren onafhankelijk van de ontwikkelomgeving. Met de compiler van Visual Basic kunnen we kiezen uit twee verschillende codes :
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
36
•
machinecode :
bestaat uitsluitend uit machine- instructies die rechtstreeks door de pocessor kunnen worden uitgevoerd
•
P-code :
een tussencode die niet door de processor maar door de runtimebibliotheek wordt uitgevoerd
Over het algemeen geldt dat de machinecode de snelste variant is.
2. Algemene opbouw van een Visual Basic-programma
Een Visual Basic-programma kan niet beschouwd worden als een samenhangend instructieblok, waarvan de instructies keurig na elkaar worden uitgevoerd. Een Visual Basic-programma is veeleer te beschouwen als een project (.VBP) dat uit verschillende programmamodulen kan bestaan.
Deze programmamodulen kunnen zijn : Formulieren (.FRM) Klassenmodulen (.CLS) Algemene modulen (.BAS) Bronbestanden (.RES) Componenten (.OCX) Designer (.DSR)
Uit welke modulen een project kan bestaan hangt af van het type programma. Voor de opbouw van de magazijnsimulator hebben we enkel gebruik gemaakt van de eerste drie programmamodulen.
3. Algemene opbouw van een module We bespreken hier de opbouw van de drie moduletypen die programma-instructies kunnen bevatten, d.w.z. een formulier, een algemene module en een klasse.
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
37
Elke module bestaat uit twee delen : een declaratiegedeelte :
bevat de declaratie van variabelen, constanten (met de
Const-instructie) en externe functies (met de Declare-instructie), kan geen instructies bevatten een proceduregedeelte :
hierin
worden
alle
procedures
en
functies
ondergebracht. Bij de procedures wordt een onderscheid gemaakt tussen enerzijds algemene procedures en functies en anderzijds gebeurtenisprocedures.
3.1
Declaratie constanten, variabelen en externe functies (het declaratiegedeelte)
3.1.1
Constanten
Constanten zijn waarden die tijdens de programma-uitvoering niet kunnen worden gewijzigd. We declareren ze als volgt : Const « naam constante » = « waarde constante »
3.1.2
Variabelen
De declaratie gebeurt als volgt : Dim(of Private of Public) « naam variabele » As « gegevenstype »
Bij de declaratie van een variabele worden dus drie dingen ‘vrijgegeven’ :
1) de naam van de variabele
2) het gegevenstype van de variabele Indien er geen gegevenstype vermeld wordt, dan wordt dit automatisch een variabele van het type Variant.
Een overzicht van de gegevenstypes die gebruikt werden bij de opbouw van de magazijnsimulator geven we in onderstaande tabel.
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
38
Tabel 3.1
Overzicht van de gegevenstypes gebruikt in magazijnsimulator
Gegevenstype
Waardebereik
Boolean
0 (False) of 1 (True). Andere waarden kan een variabele van dit type niet aannemen.
Integer
Gehele getallen van –32768 tot en met +32767.
Double
-1,79769313486232E308 tot en met -4,94065645841247E-324 en 4,94065645841247E-324 tot en met 1,79769313486232E308
String
(vaste lengte) Ongeveer 65500 tekens.
Variant
Elke numerieke waarde in het bereik van een Double-variabele.
3) het geldigheidsbereik van de variabele Het geldigheidsbereik van een variabele legt vast op welke plaatsen in het programma de variabele kan worden aangesproken, oftewel geldig is. VBA maakt bij variabelen onderscheid tussen drie verschillende geldigheidstypen :
(1) lokale variabelen op procedureniveau : deze zijn alleen beschikbaar binnen een procedure (of functie), ze worden binnen de procedure met een Diminstructie gedeclareerd
(2) lokale variabelen op moduleniveau : deze zijn geldig in de gehele module, dit wil zeggen in alle procedures en functies van die module, ze worden gedeclareerd met de instructie Private
(3) Globale variabelen : deze zijn geldig in het gehele programma, een variabele geldt als globaal als ze met de instructie Public of Dim wordt gedeclareerd binnen een module of in het declaratiegedeelte van een formulier
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
39
3.1.3
Externe functies en procedures
Het betreft hier meestal WIN32-API-oproepen. API staat voor Application Programming Interface en is in feite een verzameling van functies en procedures die de interface vormt tussen toepassingssoftware en externe software. Alle API’s zijn ondergebracht in DLL’s (Dynamic Link Library).
Indien een VB-programma een API-functie wil aanroepen dan moet deze functie aan het programma bekend gemaakt worden met een Declare-instructie, daarbij worden de naam van de procedure/functie, de naam van de DLL en het aantal argumenten met hun gegevenstype opgegeven.
In de magazijnsimulator gebruiken we één dergelijke externe functie om een delay in te voeren telkens een orderregel is afgewerkt, zodat de gebruiker de tijd heeft om de route te zien : Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
3.2
Opbouw en oproep van een procedure of functie (het proceduregedeelte)
Een
procedure
is
een
programma-instructie
die
een
reeks
instructies
vertegenwoordigt. De algemene opbouw van een procedure kan vereenvoudigd als volgt voorgesteld worden : (Private/Public) Sub « naam procedure » (argumenten (indien van toepassing)) willekeurige instructies end sub Het oproepen gebeurt op het moment dat de procedurenaam in een programma wordt aangetroffen. Daarbij worden dan al dan niet argumenten opgegeven.
Hetzelfde principe is van toepassing voor functies : (Private/Public) Sub « naam functie » (argumenten) [As gegevenstype] willekeurige instructies [naam = expressie] end function Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
40
Een gebeurtenisprocedure is een procedure die niet rechtstreeks wordt opgeroepen maar via een gebeurtenis. Elk besturingselement en elk formulier beschikt over een bepaald aantal gebeurtenissen waarop de componenten kunnen reageren (bijvoorbeeld een muisklik). Elke gebeurtenis is aan een gebeurtenisprocedure verbonden. Treedt de gebeurtenis op dan wordt door Visual Basic automatisch de bijhorende gebeurtenisprocedure in werking gesteld.
4. Class modules en objecten
Bij de opbouw van de magazijnsimulator maken we gebruik van klassen om de gegevens beter te structureren. Daarom gaan we hier wat dieper in op het werken met class modules.
4.1 •
Definities
Object :
Een programma-element in Visual Basic dat met eigenschappen en
methoden wordt aangesproken en op gebeurtenissen kan reageren. •
Klasse :
Een klasse wordt gebruikt om objecten te definiëren. Een klasse legt de
eigenschappen, methoden en eventueel ook gebeurtenissen van een object vast. •
Klassenmodule (class module) : In tegenstelling tot andere programmeertalen beschikt VBA niet over een ‘Class’-instructie. In plaats daarvan definiëren we een klasse door een klassenmodule aan het actuele project toe te voegen. Een klassenmodule is een module in een Visual Basic-project waarin de eigenschappen, methoden en gebeurtenissen van een nieuw object zijn vastgelegd.
•
UML :
Staat voor Unified Modeling Language. Het is de verzamelnaam
waaronder verschillende modelleringstools worden samengebracht.
4.2
Opbouw van een class module
Klassen zijn specificaties van objecten. Ze bestaan over het algemeen uit :
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
41
•
een naam
•
een set attributen (fields)
•
een set operaties (methoden) Constructors : initialiseren de objecttoestand Accessors : rapporteren over de objecttoestand Mutators : wijzigen de objecttoestand Destructors : vernietigen het object
We illustreren nu aan de hand van een voorbeeld hoe we de methoden van een klasseobject kunnen oproepen. Stel dat we een klasse Student hebben met als methode getNumber om het studentennummer van die student op te vragen. Verder geven we een object van de klasse student de naam ‘leerling’. Dan kunnen we het studentennummer opvragen door de instructie : leerling.getNumber. Heeft de klasse student bijvoorbeeld ook een methode setNumber om het studentennummer van een bepaalde student vast te leggen dan gebruiken we deze op de volgende manier : leerling.setNumber(« nummer ») of Call leerling.setNumber(« nummer »).
4.3
Class Diagrams (in UML)
Een Class Diagram geeft een overzicht van een systeem of model door haar klassen weer te geven en de relaties tussen deze klassen onderling. Class Diagrams zijn statisch in die zin dat ze weergeven welke interacties er bestaan, maar niet wat er gebeurt op het moment van interactie.
De opbouw van een model vormt de basis voor de objectgeoriënteerde oplossing van het probleem. Het model onttrekt de essentiële details van het onderliggende probleem uit hun doorgaans gecompliceerde omgeving.
Verschillende modelleringstechnieken worden aangeduid onder de noemer UML, wat staat voor Unified Modeling Language. Een UML-voorstelling van een klasse ziet er bijvoorbeeld als volgt uit :
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
42
Student Name : string Number : long GetName() GetNumber() Figuur 3.1
UML-voorstelling van een klasse
De voorstelling bestaat uit een rechthoek, verdeeld in drie delen : klassenaam, attributen en operaties.
Tussen de verschillende klassen kunnen drie soorten relaties bestaan : •
Association : tussen twee klassen bestaat een association als een object uit de ene klasse informatie nodig heeft van een object uit de andere klasse om zijn operaties te kunnen uitvoeren, wordt voorgesteld door een verbinding tussen de twee klassen, een pijltje geeft aan in welke richting de association kan doorlopen worden, associations zonder pijltje kunnen in beide richtingen doorlopen worden
•
Aggregation : een association waarbij één klasse tot een colletion (verzameling van klassen) behoort, wordt voorgesteld door een verbinding met een ruitvormig einde dat gericht is naar de klasse die het geheel omvat
•
Generalization : een relatie waarbij de ene klasse een subklasse is van de andere, wordt voorgesteld door een verbinding eindigend op een driehoekige punt die gericht is naar de superklasse
De multipliciteit, aangegeven aan beide eindpunten van een association, is het aantal mogelijke objecten van de klasse aan dat eind geassocieerd met één object van de klasse aan het andere eind.
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
43
Tabel 3.2
Meest voorkomende multipliciteiten in een Class Diagram
Multiplicitiet
Betekenis
0..1
0 tot1 object
0..* of *
geen limiet op het aantal objecten
1
precies 1 object
1..*
ten minste 1 object
n..m
n tot m objecten
Het eindpunt van een association kan een ‘role name’ hebben om de aard van de association te verduidelijken.
5. Grafische methoden in Visual Basic
Het tekenen van lijnen en andere geometrische figuren wordt uitgevoerd met figuurelementen, grafische methoden of Windows-API-functies. Alle grafische methoden hebben met elkaar gemeen dat ze met precies dezelfde syntaxis op drie objecten kunnen worden toegepast : het formulier, het afbeeldingsveld en de printer. In de magazijnsimulator hebben we enkel gebruikt gemaakt van de grafische methode line :
Tabel 3.3
De grafische methode Line
Methode Syntaxis
Betekenis
Line
[step](x1,y1)-
Tekent een lijn of een rechthoek als ‘B’ is toegevoegd.
[step](x2,y2),
De aanvulling ‘F’ vult de rechthoek met de kleur die
Kleur, (BF)
met het argument Kleur is vastgelegd. Zonder ‘F’ wordt alleen de kleur van het kader weergegeven.
We gebruiken deze methode bij het tekenen van de grid op het scherm:
picLayout.Line(x1,y1)-(x2,y2), color, BF
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
44
Met deze instructie tekenen we een rechthoek waarvan de linkerbovenhoek wordt vastgelegd door de coördinaten (x1,y1) en de rechterbenedenhoek door de coördinaten (x2,y2). Indien de ‘F’ wordt weggelaten dan tekenen we een niet opgevulde rechthoek, dus enkel het kader.
Tot slot gaan we dieper in op het werken met het intern coördinatenstelsel. Elk uitvoervak beschikt over een eigen intern coördinatensysteem. Met de Scale-methode kunnen we een eigen onderverdeling aanbrengen in de x- en y-as. picLayout.Scale (0,0) – (dc.getBreedte( ),
dc.getDiepte( ) ) zorgt ervoor dat de
oorsprong de coördinaten (0,0) heeft en de x-as en de y-as respectievelijk uit zoveel eenheden bestaan als de breedte en diepte van het magazijn. Verder gebruiken we de eigenschappen ScaleHeight, ScaleLeft, ScaleTop en ScaleWidth om er voor te zorgen dat de grafsiche methoden niet afhankelijk zijn van vaste coördinaten. ScaleHeight en ScaleWidth geven de lengte van de x- en y-as aan, in de eenheid ingesteld met de ScaleMode-eigenschap. ScaleLeft en ScaleTop leggen de coördinaten van de oorsprong vast. Daarbij merken we op dat de oriëntatie van het assenkruis volgens de Amerikaanse standaard gebeurt. We noemen dit een linkshandig of ‘lefthanded’ assenstelsel.
X-as 4 1 3
Afbeeldingsveld 2
Y-as
Figuur 3.2
1 = ScaleHeight 2 = ScaleWidth 3 = ScaleLeft 4 = ScaleTop
Betekenis van de eigenschappen ScaleHeight, ScaleWidth, ScaleLeft en ScaleTop
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
45
6. Microsoft Excel Object Model
6.1
Definitie DOM
Het Document Object Model (DOM) is een Application Programming Interface (API) voor documenten. De naam Document Object Model werd gekozen omdat het een ‘object model’ is in de traditionele object-georiënteerde zin : documenten worden gemodelleerd door het gebruik van objecten en het model omvat niet alleen de structuur van een document, maar ook het gedrag van het document en de objecten waaruit het is samengesteld.
In het DOM hebben de documenten een logische structuur die erg lijkt op een boomstructuur. Het DOM specifieert niet dat documenten moeten geïmplementeerd worden als een boom en specifieert ook niet hoe de relaties tussen de objecten moeten geïmplementeerd worden. Het DOM is dus een logisch model dat op elke gewenste manier kan geïmplementeerd worden. We gebruiken de term structure model om de boomstructuur van een model te beschrijven.
Als object model identificeert het DOM : •
de interfaces en objecten die gebruikt worden om documenten voor te stellen en te manipuleren
•
de semantiek van deze interfaces en objecten (inclusief gedrag en attributen)
•
de relaties en interacties tussen deze interfaces en objecten
6.2
Gebruik van Excel Object Model
In de magazijnsimulator wordt gebruik gemaakt van het Excel Object Model voor de input (klasse Invoer) en output (klasse Uitvoer). We bespreken hier de gebruikte objecten en hun methoden en eigenschappen.
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
46
6.2.1
Excel Application Object
Het Excel Application Object is het top-level object in het Excel Object Model. Het Application Object wordt gebruikt om application-level eigenschappen (properties) te specifiëren en application-level methoden uit te voeren, het is tevens het toegangspunt tot de rest van het Excel Object Model. Om vanuit Visual Basic te kunnen werken met Excel, moeten we een object variabele definiëren die het Excel Application Object voorstelt. Daarmee kunnen we de gewenste Excel-file openen en sluiten. Dit gebeurt met de volgende instructies :
Declaratiegedeelte :
Private xlApp as Excel.Application
Proceduregedeelte :
Set xlApp = New Excel.Application (openen) xlApp.quit (sluiten)
6.2.2
Workbook Object
Het Workbook Object stelt een Excel .xls of .xla workbook file voor. We gebruiken het Workbook Object om met één enkel Excel Workbook te werken. Dit gebeurt met de volgende instructies :
Declaratiegedeelte :
Private xlWb as Excel.Workbook
Proceduregedeelte :
Set xlWb = xlApp.Workbooks.Open ("bestandsnaam van de te openen file")
6.2.3
Worksheet Object
Het Worksheet Object en het Range Object (zie 6.2.4) zijn de meest essentiële en belangrijkste componenten van elke application die we binnen Excel creëren. Elke Excel Workbook kan één of meerdere Excel Worksheets bevatten. We kunnen dan van activesheet, d.w.z. het blad (of sheet) waarin we momenteel werken, veranderen door gebruik te maken van de volgende instructies :
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
47
Declaratiegedeelte :
Private xlWs as Excel.Worksheet
Proceduregedeelte :
xlApp.Worksheets("naam sheet").Activate Set xlWs = xlWb.Activesheet
6.2.4
Range Object
Een Range Object kan één enkele cel zijn of een verzameling van cellen. Het kan één enkel object zijn of een verzameling van objecten. Het kan een rij of kolom zijn en het kan ook een driedimensionale verzameling van cellen voorstellen die meerdere worksheets overspannen. Range Property Zowel het Application Object, het Workbook Object als het Range Object hebben een Range Property. We gebruiken de Range Property om de waarde van een cel in te lezen en om een waarde weg te schrijven naar een cel, waarbij de cel wordt aangeduid met een naam. Tevens gebruiken we de Range Property om het huidige rij- of kolomnummer een bepaalde waarde te geven.
Instructies : ingelezen waarde = xlWs.Range("naam").Value xlWs.Range("naam").Value = weg te schrijven waarde rijnummer = xlWs.Range("naam").row kolomnummer = xlWs.Range("naam").column
In dit geval fungeert de Range Property als een referentie naar een bepaalde cel van de actieve sheet. Cells Property We kunnen de Cells Property gebruiken om door een bereik van cellen te lopen of te refereren naar een bereik van cellen, door gebruik te maken van numerieke rij- en kolomaanduidingen. In de magazijnsimulator gebruiken we de Cells Property om met behulp van één celnaam alle gegevens over de rekken en orders automatisch in te Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
48
lezen waarbij het aantal cellen dat moet ingelezen worden niet gekend is voor de start van de input. Tevens gebruiken we de Cells Property voor het wegschrijven naar opeenvolgende cellen tijdens de uitvoer.
Instructies : « ingelezen waarde » = xlApp.Activesheet.Cells(rij, kolom).Value xlWs.Cells(rij ,kolom) = « weg te schrijven waarde »
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
49
Figuur 3.3
Excel Object Model
Hoofdstuk 3 : Gebruik van Visual Basic 6.0 en Microsoft Excel
50
Hoofdstuk 4 :
Opbouw magazijnsimulator
1. Inleiding
In dit hoofdstuk bespreken we de opbouw van de magazijnsimulator.
De
magazijnsimulator is opgebouwd uit 7 VB-klassen en een hoofdprogramma (magazijnsimulator.frm). We schetsen de samenhang tussen de verschillende klassen en het hoofdprogramma door middel van een class diagram.We bespreken de procedures die in de verschillende klassen en het hoofdprogramma gebruikt worden, alsook de opbouw van het in- en uitvoerbestand. We geven ook aan welke constanten in elke klasse moeten gedefinieerd worden. We merken daarbij nu al op dat verschillende constanten meerdere keren moeten gedefinieerd worden. Dit omwille van de beperking van Visual Basic op vlak van constanten, in die zin dat het niet mogelijk is een constante één keer te definiëren en ze dan in elke klasse te gebruiken. Voor de programmeer-technische aspecten van de procedures verwijzen we naar het vorige hoofdstuk en de programmacode in Appendix B.
2. Veronderstellingen en concept achter magazijnsimulator
Het programma is ontworpen om een magazijn met meerdere pickgangen en dwarsgangen te kunnen simuleren. Daarbij zijn we uitgegaan van een aantal algemene veronderstellingen en een bepaald concept, die we eerst nader toelichten.
2.1
Veronderstellingen
2.1.1
Magazijnlayout
•
We redeneren op een grid die gegenereerd wordt op basis van de afmetingen van het magazijn en de opgegeven lengte van de zijde van een cel van de grid. Deze zijde is dan ook bepalend voor de breedte of lengte van een gang of rek.
Hoofdstuk 4: Opbouw magazijnsimulator
51
•
Het magazijn heeft één dummylocatie waar alle in- en uitslagprocessen starten en eindigen, met name één plaats waar de verzamelronde (of wave) start met het ophalen van de orderspecificaties en eindigt met het afzetten van de gepickte goederen. Dit punt noemen we het centraal afgiftepunt (CAP).
•
Bij elk rek hoort één bepaalde pickplaats.
•
Voor het aanduiden van de locaties van rekken, pickplaatsen en het CAP geven we de x- en y-coördinaten van de middelpunten op.
•
Tussen twee blokken van rekken bevindt zich een enkele of een dubbele pickgang. In het eerste geval kan vanop elke pickplaats uit twee rekken producten genomen worden. In het tweede geval kan vanop elke pickplaats maar uit één rek gepickt worden. In de grid komt dit respectievelijk overeen met één rij of kolom en twee rijen of kolommen tussen twee blokken van rekken. Onderstaande figuren verduidelijken het verschil tussen beide situaties. Daarbij worden de karakteristieke afmetingen van het magazijn aangeduid.
Figuur 4.1
Magazijnlayout met enkele pickgang
Hoofdstuk 4: Opbouw magazijnsimulator
52
Figuur 4.2 2.1.2 •
Magazijnlayout met dubbele pickgang
Orderverzamelen
Het orderverzamelen gebeurt volgens het principe van wave-picking. Elke wave of verzamelronde is samengesteld uit een aantal orders die op hun beurt bestaan uit orderregels met daarop de specificaties van het te picken product. Deze specificaties vermelden het productnummer, het aantal eenheden dat moet gepickt worden en eventueel het rek waarin dat product is opgeslagen. Dit om situaties op te vangen waarbij éénzelfde product in meerdere rekken te vinden is.
•
De orders worden afgehandeld in de volgorde waarin ze worden opgegeven.
•
De pickrondes worden uitgevoerd door één orderverzamelaar. De situatie waarbij meerdere orderverzamelaars tegelijk actief zijn in hetzelfde magazijn of dezelfde zone is dus niet gemodelleerd.
•
De items die moeten gepickt worden zijn steeds in voldoende mate aanwezig.
•
Het sorteren van de artikels over de verschillende orders is niet gemodelleerd.
2.1.3 •
Routering
We zoeken het lokaal optimum in die zin dat we tussen twee opgegeven pickplaatsen de kortste route zoeken. De optimalisering van de picking-volgorde valt buiten de scope van deze scriptie.
Hoofdstuk 4: Opbouw magazijnsimulator
53
•
In de magzijnlayout voeren we op elke kruising van een pickgang en dwarsgang een checkpunt (checkpoint) of knoop in. Deze knoop is een punt waarlangs we zeker moeten passeren indien we in een bepaalde pickgang een picking willen uitvoeren. Dit is geen fysiek punt in het magazijn, maar op die manier bekomen we een netwerk van knopen waarin we de kortste route kunnen bepalen door toepassing van het kortste pad algoritme van Dijkstra. Ook kunnen we op die manier de complexiteit van het probleem wat reduceren omdat het aantal knopen relatief beperkt blijft. Volgende figuren geven een voorbeeld van waar dergelijke knopen moeten ingevoerd worden.
Figuur 4.3
•
•
•
• •
• •
• •
Plaats knopen in magazijnlayout met enkel pickgang
•
•
•
•
•
•
• •
• •
• •
• •
• •
• •
Figuur 4.4
Plaats knopen in magazijnlayout met dubbele pickgang
De aanduiding van de plaats van de knopen gebeurt opnieuw door opgeven (in het invoerbestand) van x- en y-coördinaten van de punten. Deze coördinaten moeten steeds overeenkomen met de coördinaten van het middelpunt van één van de cellen van de grid. •
Het intern transportmaterieel kan niet gelijktijdig horizontaal en verticaal bewegen op de grid, maar rijdt routes conform ‘Manhattan’-afstanden in plaats van de
Hoofdstuk 4: Opbouw magazijnsimulator
54
minder waarschijnlijke Euclidische afstanden. We verduidelijken dit met onderstaande figuur.
Manhattanafstand (x2, y2)
(x1,y1)
Figuur 4.5
Euclidische afstand
Onderscheid Manhattan- en Euclidische afstand
De Euclidische afstand bedraagt hier √(x2 - x1)2 + (y2- y1)2 en de Manhattanafstand bepalen we door |x2 - x1| + |y2- y1|. •
Bij het zoeken en samenstellen van de route bewegen we telkens van het middelpunt van de ene cel naar het middelpunt van de andere in het rooster.
2.1.4 •
Infrastructuur voor materiaalbehandeling
Er is slechts één intern transportmiddel in gebruik in het magazijn. Dit is een veronderstelling die niet overeind blijft in een reëel systeem. In systemen met meerder orderpickers kunnen bijvoorbeeld opstoppingen voorkomen in gangen met een hoge pickactiviteit, waardoor uiteraard de performantie van het picken degradeert. Ook moeten de pickers op hetzelfde traject beter met elkaar communiceren .
•
Er wordt slechts één soort opslagsysteem gebruikt.
Hoofdstuk 4: Opbouw magazijnsimulator
55
2.2
Concept
We vertrekken van een Excel-file met daarin alle relevante gegevens met betrekking tot de magazijnlayout, de orderspecificaties en de matriaalbehandeling. Op basis van de informatie over de magazijnlayout genereren we een grid waarop we zullen werken voor het uitvoeren van de verzamelrondes. De pickings worden dan uitgevoerd in de volgorde zoals ze zijn opgegeven in het invoerbestand. Tussen elke twee pickplaatsen of tussen een pickplaats en het CAP bepalen we het korste pad. Daarbij maken we onderscheid tussen drie gevallen. Enerzijds de gevallen waarbij twee opeenvolgende pickplaatsen in dezelfde (enkele of dubbele) pickgang liggen en anderzijds het geval waarbij van pickgang moet gewisseld worden. In het eerste geval bepalen we het kortste pad door het zoeken van de rechtstreekse verbinding tussen beide plaatsen, in het tweede geval passen we Dijkstra’s algoritme toe waarbij we het kortste pad zoeken in het netwerk van knopen die in de layout werden ingevoerd. In wat volgt noemen we het pad tussen twee pickplaatsen een route. Per oderregel berekenen we de afgelegde afstand in het magazijn, de tijd nodig om met het gebruikte transportmiddel die afstand af te leggen en te totale picktijd of doorlooptijd voor een orderregel. Ook wordt de rekinhoud verminderd met de te picken hoeveelheid. De simulatie wordt gevisualiseerd door middel van grid op het scherm, waarbij kleurencodes de toestand van de cellen (die overeenkomen met rekken, gangen of het CAP) verduidelijken. Tot slot worden alle relevante gegevens over de waves of verzamelrondes in een Excel-file geschreven.
3. Input
Het invoerbestand is opgebouwd uit negen sheets of werkbladen, die elk een specifiek gedeelte van de informatie bevatten : 1. Layout 2. Knopen 3. Orders 4. Transport 5. Netwerk 6. Schema1 Hoofdstuk 4: Opbouw magazijnsimulator
56
7. Schema2 8. Schema3 9. Schema4
We geven een overzicht van de structuur van elk van deze werkbladen waarbij we ook bijzondere aandacht besteden aan de naam van de cellen.
3.1
layout
Hier geven we de breedte (in meter), de diepte (in meter), de lengte van de zijde van de cel (in meter) en het aantal rekken op. Voorts bevat deze sheet een tabel met daarin per rek het reknummer, coördinaten van het middelpunt van het rek en van het middelpunt van de bijhorende pickplaats (in meter), het nummer van het product opgeslagen in het rek en het aantal eenheden of SKU’s. Ook geven we de coördinaten van het CAP op. Onderstaande figuur geeft een overzicht van de namen die aan de cellen moeten gegeven worden (deze staan aangegeven tussen aanhalingstekens en zijn cursief gedrukt).
breedte (m) diepte (m) lengte zijde (m) aantal rekken
18 (“breedte”) 10,5(“diepte”) 1,5(“lengtezijde”) 24(“aantalrekken”)
Rekken(“rekken”) x rek y rek x pickplaats Y pickplaats product aantal 1 0,75 2,25 2,25 2,25 1 100 2 0,75 3,75 2,25 3,75 2 100 3 0,75 5,25 2,25 5,25 3 100 Afgiftepunt
x CAP y CAP 11,25(“xCAP”) 9,75(“yCAP”) Figuur 4.6
Celnamen in layout-sheet
Hoofdstuk 4: Opbouw magazijnsimulator
57
Figuur 4.7
3.2
Verband tussen coördinaten en layout
knopen
Deze sheet bevat eerst en vooral de aanduiding van het aantal knopen of checkpunten dat we in de magazijnlayout hebben ingevoerd en daarnaast een tabel met de coördinaten van elk van die knopen. Onderstaande figuur geeft opnieuw een overzicht van de verschillende celnamen.
18(“aantalcheckpoints”)
aantal checkpoints Checkpoint (“checkpoint”)
x
y
1 2 Figuur 4.8
2,25 3,75
0,75 0,75
Celnamen in knopen-sheet
Hoofdstuk 4: Opbouw magazijnsimulator
58
3.3
orders
Dit werkblad bevat alle orderspecificaties. Daarmee bedoelen we het nummer van de wave en het order waartoe een orderregel behoort en het productnummer, het aantal eenheden en het reknummer van het te picken product.
Voor de aanduiding van de orderspecificaties wordt een bepaalde methodiek gebruikt: •
het begin van een wave of verzamelronde wordt aangeduid door de celnaam “wave(nummer wave)”
•
het aantal orders behorende tot die wave wordt aangeduid door de celnaam “orders(nummer wave)”
•
het aantal orderregels waaruit een order van de wave is opgebouwd wordt aangeduid door de celnaam “orderregels(nummer order)wave(nummer wave)”
We verduidelijken dit opnieuw met een figuur: aantal waves
2(“aantalwaves”)
wave aantal orders Order Aantal orderregels 1(“wave1”) 2(“orders1”) 1 6(“orderregels1wave1”) Figuur 4.9
3.4
Celnamen in orders-sheet
transport
De snelheid (in m/s) waarmee men zich door het magazijn kan bewegen, m.a.w. de snelheid van het intern transportmiddel en de gemiddelde tijd nodig om te picken (in s), die bepaald wordt door de hef- en daalsnelheid van het intern transportmaterieel en het soort opslagsysteem, vinden we in deze sheet terug. Voor de precieze bepaling van de snelheid en de gemiddelde picktijd verwijzen we naar hoofdstuk 2 en hoofdstuk 6.
snelheid (m/s) picktijd (s) Figuur 4.10
3(“snelheid”) 18(“picktijd”)
Celnamen in transport-sheet
Hoofdstuk 4: Opbouw magazijnsimulator
59
3.5
netwerk
Deze sheet geeft de rechtstreekse verbindingen in het netwerk van knopen weer. We doen dit door middel van een rooster waarvan het principe in onderstaande figuur wordt weergegeven:
van\naar
Nummer knoop (n)
nummer knoop(m) Afstand tussen de twee knopen Figuur 4.11
Principe netwerkrooster
Dit betekent dat de afstand van knoop m (rij) naar knoop n (kolom) opgegeven is in de cel op de kruising van deze rij en kolom. Bevat de desbetreffende cel van het rooster geen waarde dan betekent dit dat er geen rechtstreekse verbinding bestaat tussen deze twee knopen. Op de diagonaal van dit rooster staan enkel nullen, aangezien er in theorie wel een rechtstreekse verbinding bestaat van een knoop naar zichzelf, maar dat de afstand daarbij 0 is. Het rooster is symmetrisch, het spreekt immers voor zich dat de afstand van knoop m naar knoop n gelijk is aan de afstand van knoop n naar knoop m. Om die reden worden enkel afstandswaarden opgegeven boven de diagonaal van het rooster. In het programma zorgen we ervoor dat de waarde van cel(n,m) gelijk gesteld wordt aan die van cel(m,n) indien de eerste cel zich onder de diagonaal bevindt en de tweede boven de diagonaal.
Netwerk(“netwerk”) 1 2 3 Figuur 4.12
1 0
2 1,5 0
3
4
4,5 0
1,5
Celnamen in netwerk-sheet
Hoofdstuk 4: Opbouw magazijnsimulator
60
3.6
schema1, schema2, schema3, schema4
Deze vier werkbladen worden gebruikt om de resultaten van het kortste pad algoritme van Dijkstra voorlopig op te slaan. Dit gebeurt in zes kolommen waar per knoop respectievelijk het nummer, de x-coördinaat, de y-coördinaat, de toestand van het label (0 = geen, 1 = tijdelijk, 2 = permanent), de vorige knoop in het algoritme en de afstand vanaf de startknoop worden opgeslagen. Deze tabellen wordt dan eerst en vooral gebruikt om de afstandswaarden terug te vinden tijdens het bepalen van de kortste route. Eens de kortste route bepaald is, gebruiken we deze tabellen om alle knopen die deel uitmaken van die kortste route terug te vinden. Voor de meer gedetailleerde uitleg van de bovenstaande begrippen verwijzen we naar hoofdstuk 5.
(“schema(nummer)”) Nummer X Figuur 4.13
Y
Status
Previous Distance
Celnamen in schema-sheets
4. Class Diagram Magazijnsimulator Invoer
Magazijn
Knoop
Order
Route
Dijkstra
Hoofdprogramma
Uitvoer
Figuur 4.14
Class Diagram Magazijnsimulator
Hoofdstuk 4: Opbouw magazijnsimulator
61
Bovenstaande figuur schetst de samenhang tussen de verschillende klassen en het hoofdprogramma
5. Klasse Invoer (Invoer.cls)
5.1
Opbouw
Deze klasse wordt gebruikt om alle gegevens aangaande de layout van het magazijn, het knopennetwerk, de orderspecificaties, het intern transportmaterieel en de opslagsystemen in te lezen.
Wanneer we de magazijnsimulator starten wordt eerst een object van de klasse Invoer aangemaakt. Dit object geven we de naam ‘inputdata’. Bij het aanmaken van het object inputdata verschijnt eerst een inputbox waar de naam van het invoerstand moet worden ingegeven. Deze wordt met de methode setBestandsnaam opgeslagen. Met de methode App.Path wordt automatisch de locatie van dit bestand in het computergeheugen opgespoord door de compiler. Dan wordt het invoerbestand geopend met de methode aangehaald in hoofdstuk 3 (zie 6.2.1 en 6.2.2) en waarbij de bestandsnaam wordt meegegeven door de methode getBestandsnaam. Vervolgens worden de volgende procedures uitgevoerd waarbij er telkens voor gezorgd wordt dat het juiste werkblad van het invoerbestand geactiveerd is : •
Afmetingen_magazijn
We lezen de basisafmetingen van het magazijn in. Dit wil zeggen de breedte, de diepte en de afmeting van de cellen waarin de plattegrond van het magazijn zal opgedeeld worden wanneer in de klasse Magazijn de grid gegenereerd wordt. Dit gebeurt met de methoden setBreedte, setDiepte en setZijdecel. Bijvoorbeeld : de instructie Call Me.setBreedte(xlWs.Range("breedte").Value) stelt de waarde van de breedte gelijk aan de waarde die zich in de cel met de naam breedte bevindt.
Hoofdstuk 4: Opbouw magazijnsimulator
62
•
Rekken
Met deze procedure lezen we eerst het aantal rekken in en vervolgens alle gegevens van de verschillende rekken. Daarvoor gebruiken we de methoden setAantalRekken en setReklijst. De gegevens worden opgeslagen in de tweedimensionale lijst ‘reklijst’. De eerste dimensie van deze lijst geeft aan om welk rek het gaat, de tweede dimensie bestaat uit 7 niveaus : (1) het nummer van het rek in kwestie (2) de x-coördinaat van het midden van het rek (3) de y-coördinaat van het midden van het rek (4) de x-coördinaat van het midden van de pickplaats die bij het rek hoort (5) de y-coördinaat van het midden van de pickplaats die bij het rek hoort (6) het product dat zich in het rek bevindt (aangeduid door een nummer) (7) het aantal eenheden van dat product die in het rek zitten •
Plaats_CAP
Hier lezen we de x- en y-coördinaat van het Centraal Afgiftepunt (CAP) in. Dit is zoals vermeld eerder in dit hoofdstuk en in hoofdstuk 2 de plaats waar de goederen of producten na de picking worden afgeleverd voor sortering en/of verzending. De coördinaten worden bijgehouden in een ééndimensionale lijst ‘CAP’ waarin het eerste niveau de x-coördinaat voorstelt en het tweede niveau de y-coördinaat. Dit doen we met de methode setCAP. •
Checkpoints
We lezen het nummer en de coördinaten van de checkpunten of knopen in en slaan ze met de methode setCheckpointslijst op in een tweedimensionale lijst ‘checkpointlijst’ waarbij de eerste dimensie het nummer van de knoop weergeeft en de tweede dimensie uit drie niveaus bestaat : (0) het nummer van de knoop (1) de x-coördinaat van de knoop (2) de y-coördinaat van de knoop
Hoofdstuk 4: Opbouw magazijnsimulator
63
•
Aantal_waves
Het aantal waves waaruit de orderverzamelllijst bestaat inlezen en opslaan met de methode setWaves. •
Aantal_orders
We lezen het aantal orders in waaruit een bepaalde wave is opgebouwd. Dit wordt met setOrders opgeslagen in de ééndimensionale lijst ‘orders’ waarbij het niveau gelijk is aan het nummer van de wave. •
Aantal_orderregels_per_order
Hier maken we met setOrderregels een tweedimensionale lijst ‘orderregels’ aan om het aantal orderregels van een order van een bepaalde wave bij te houden. De eerste dimensie geeft het nummer van de wave aan, de tweede het ordernummer. •
Orderlijst_vastleggen
In deze procedure lezen we de orderspecificaties in en maken we met de methode setOrderlijst een vierdimensionale lijst aan om deze specificaties bij te houden. De eerste drie dimensies geven respectievelijk het wavenummer, het ordernummer en het orderregelnummer aan. De vierde dimensie omvat drie niveaus : (1) het nummer van het product dat we moeten picken (2) het aantal eenheden dat we moeten picken (3) het nummer van het rek waar we het te picken product kunnen vinden (dit om situaties waarbij eenzelfde product in meerdere rekken zit op te vangen) •
Snelheid_en_picktijd
Deze procedure laat ons toe de snelheid van het intern transportmiddel en de gemiddelde picktijd, die bepaald wordt door het type transportmiddel en het gebruikte opslagsysteem, in te lezen en bij te houden met de methoden setSnelheid en setPicktijd.
Na het uitvoeren van deze procedures wordt het invoerbestand opnieuw gesloten.
Hoofdstuk 4: Opbouw magazijnsimulator
64
5.2
Instellingen
Om de magazijnsimulator naar behoren te laten werken moeten in de klasse Invoer een aantal constanten ingesteld worden.
We moeten vijf constanten definiëren : (1) maxrekken : het maximaal aantal rekken in het magazijn (2) maxknopen : het maximaal aantal knopen dat in de magazijnlayout kan voorkomen (3) maxwaves : het maximaal aantal waves (4) maxorders : het maximaal aantal orders per wave (5) maxorderregels : het maximaal aantal orderregels per order
Hoe we respectievelijke waarden van deze constanten moeten instellen hebben we reeds in hoofdstuk 3 (zie 3.1.1 Constanten).
6. Klasse Magazijn (Magazijn.cls)
6.1
Opbouw
Nadat alle invoergegevens zijn ingelezen in het object inputdata maken we een object aan van de klasse Magazijn dat we de naam ‘dc’ geven. In object dc van de klasse Magazijn worden alle gegevens met betrekking tot de magazijnlayout gestructureerd. Een gedeelte van de procedures heeft tot doel gegevens over te hevelen of door te geven vanuit de klasse Invoer naar de klasse Magazijn. Ook voeren we hier de bewerkingen door om een grid of rooster te genereren van de plattegrond van het magazijn, bestaande uit vierkante cellen. Daartoe berekenen we het aantal rijen en kolommen, alsook de x- en y- coördinaten van de middelpunten van de verschillende cellen. Alle gegevens met betrekking tot deze cellen houden we bij in de driedimensionale lijst ‘matrix’. De eerste twee dimensies geven hier respectievelijk het rijnummer en het kolomnummer weer. De derde dimensie bestaat uit vier niveaus :
Hoofdstuk 4: Opbouw magazijnsimulator
65
(1) de x-coördinaat van de cel (2) de y-coördinaat van de cel (3) een waarde die aangeeft of de cel deel uitmaakt van een gang of een rek, dit wordt respectievelijk aangeduid met een 0 of een 1 (4) een waarde die de ‘toestand’ van de cel weergeeft, dit heeft enkel belang voor de output naar het scherm (zie functie getcolor)
Met de procedure ‘init (invoerobject As Invoer)’ wordt het object dc van de klasse Magazijn geïnitialiseerd. Daarbij geven we als argument de naam van een object van de klasse Invoer mee. In ons geval draagt het Invoer-object de naam ‘inputdata’. Bij het initialiseren van het object worden achtereenvolgens de volgende procedures uitgevoerd : •
Afmetingen_magazijn
Hier nemen we de basisafmetingen van het magazijn over uit het object inputdata. Dit gebeurt met de methoden setBreedte, setDiepte en setZijdecel van objet dc en getBreedte, getDiepte en getZijdecel van object inputdata. •
Berekening_aantal_rijen_en_kolommen
We berekenen het aantal rijen en kolommen van de matrixstructuur of rooster door respectievelijk de diepte en de breedte te delen door de zijde van de cel. De waarden slaan we op in het object dc met de methoden setRijen en setKolommen. •
X_coördinaat
Bij de berekening van de x-coördinaten van de middelpunten van de cellen, vertrekken we van de x-coördinaat van de cel op de eerste rij en eerste kolom. Deze bedraagt de helft van de zijde van een cel. Dit is dan meteen ook de x-coördinaat van alle cellen in de eerste kolom, ongeacht de rij. We berekenen dan de x-coördinaten van alle cellen op een bepaalde rij als volgt :
x-coördinaat cel(rij,kolom) = x-coördinaat(eerste rij, eerste kolom) + ((kolom-1) * zijde cel)
Deze coördinaten worden met setMatrix(rij, kolom, 3, x) opgeslagen in de lijst matrix.
Hoofdstuk 4: Opbouw magazijnsimulator
66
•
Y_coördinaat
Ook om de y-coördinaat van elke cel te berekenen, starten we met het bepalen van de y-coördinaat van de cel op de eerste rij en eerste kolom. Deze is ook gelijk aan de helft van de zijde van een cel. Deze waarde is dan de y-coördinaat van alle cellen op de eerste rij ongeacht de kolom. De y-coördinaten van alle cellen kunnen we dan berekenen met :
y-coördinaat cel(rij,kolom) = y-coördinaat(eerste rij, eerste kolom) + ((rij-1)*zijde cel)
Deze coördinaten worden met setMatrix(rij, kolom, 2, y) opgeslagen in de lijst matrix. •
Rekken
We nemen de gegevens uit de reklijst van het Invoer-object over in de reklijst van het Magazijn-object. Daarna bepalen we welke cellen in het rooster dezelfde x- en ycoördinaten hebben als de rekken. Deze cellen worden vastgelegd in de lijst matrix (matrix(rij,kolom,3) = 1). Het rij- en kolomnummer worden vastgelegd in twee extra niveaus van de reklijst : (1) - (7) identiek aan reklijst van het Invoer-object (8) rij waarop het rek zich bevindt (9) kolom waarop het rek zich bevindt
De gebruikte methoden zijn setReklijst, setMatrix en setAantalrekken van het object dc en getReklijst en getAantalrekken van het object inputdata. •
Plaats_CAP
De coördinaten van het centraal afgiftepunt nemen we over uit de lijst CAP van het Invoer-object. Aan de lijst CAP van het Magazijn-object voegen we ook het rij- en kolomnummer van de cel toe waarop het CAP zich bevindt. (1) – (2) identiek aan de lijst CAP van het Invoer-object (3) rij waarop het CAP zich bevindt (4) kolom waarop het CAP zich bevindt
Hoofdstuk 4: Opbouw magazijnsimulator
67
Dit doen we door te bepalen welke cel dezelfde coördinaten heeft als het depot. We merken hierbij op dat het CAP beschouwd wordt als een gedeelte van een gang (matrix(rij,kolom,3) = 0). •
Checkpoints
Met de methoden getCheckpoints en getCheckpointlijst van het Invoer-object en de methoden setCheckpoints en setCheckpointlijst nemen we de gegevens aangaande de knopen over. Daarnaast voegen we twee niveaus toe aan de checkpoinslijst van het object dc : (0) - (2) identiek aan de checpointlijst van het Invoer-object (3) rij waarop de knoop in kwestie zich bevindt (4) kolom waarop de knoop in kwestie zich bevindt •
Snelheid_en_picktijd
De gegevens aangaande het intern transportmaterieel en de opslagsystemen halen we uit het Invoer-object en slaan we op in het Magazijn-object. •
getColor (public function)
Deze functie geeft op basis van de ‘toestand’ van een cel de kleur terug waarin de cel wordt afgebeeld. De toestand kunnen we bepalen aan de hand van de waarde van matrix(rij, kolom, 4). Onderstaande tabel geeft een overzicht van de mogelijke toestanden. Tabel 4.1
Mogelijke toestanden van een cel in de grid
Waarde matrix(rij,kolom,4) Betekenis 0
Cel = deel van een gang
1
Cel = rek
2
Cel = centraal afgiftepunt
3
Cel = deel van een route afgelegd tijdens afwerking orderregel
4
Cel = rek waaruit moet gepickt worden tijdens pickronde
5
Cel = pickplaats horende bij rek waaruit gepickt moet worden
Hoofdstuk 4: Opbouw magazijnsimulator
68
6
Cel = rek waaruit gepickt werd
7
Cel = volgende pickplaats van pickronde (of CAP in geval van afgifte)
6.2
Instellingen
Opnieuw moeten we een aantal constanten definiëren : (1) maxrijen : het maximaal aantal rijen van het rooster (2) maxkolommen : het maximaal aantal kolommen van het rooster (3) maxrekken : het maximaal aantal rekken in het magazijn (4) maxknopen : het maximaal aantal knopen dat in de magazijnlayout kan voorkomen Het instellen van de gewenste waarde hebben we reeds beschreven in hoofdstuk 3 (zie 3.1.1 Constanten).
7. Klasse Order (Order.cls)
7.1
Opbouw
Deze klasse heeft tot doel alle gegevens in verband met orderspecificaties te structureren. Daarmee bedoelen we het aantal waves (of verzamelrondes), het aantal orders per wave, het aantal orderregels per order van een wave en het de eigenlijke inhoud van de orderregels. Al deze gegevens hebben we ingelezen bij de aanmaak van een object van de klasse Invoer en moeten we dus enkel copiëren naar het object van de klasse Order. Wel merken we op dat dimensie 2 van de lijst orderregels nu begint met een niveau 0 om redenen die aan bod komen bij de opbouw van de klasse Route.
Het Order-object geven we de naam ‘picklijst’. Om dit object te initialiseren maken we gebruik van de procedure ‘init (invoerobject As Invoer)’, waarbij dus opnieuw als argument het Invoer-object wordt meegegeven, het copiëren van de gegevens gebeurt met de volgende procedures :
Hoofdstuk 4: Opbouw magazijnsimulator
69
•
Aantal_waves_en_orders_en_orderregels
•
Orderlijst_vastleggen
7.2
Instellingen
De constanten die we in deze klasse moeten definiëren zijn : (1) maxwaves : het maximaal aantal waves (2) maxorders : het maximaal aantal orders per wave (3) maxorderregels : het maximaal aantal orderregels per order
Het instellen van de gewenste waarde gebeurt opnieuw zoals beschreven in hoofdstuk 3 (zie 3.1.1 Constanten).
8. Klasse Route (Route.cls)
8.1
Opbouw
We gebruiken de klass Route om alle gegevens te structureren die verband houden met de routes die door de orderverzamelaar moeten afgelegd worden in het magazijn. We noemen het Route-object ‘weg’ en het initialiseren van het Route-object gebeurt met de procedure ‘init (magazijnobject As Magazijn, orderobject As Order)’, waarbij als argumenten een Magazijn-object en een Order-object worden meegegegeven. In ons geval zijn de argumenten respectievelijk ‘dc’ en ‘picklijst’. Bij het aanmaken van een object van de klasse Route worden eerst en vooral de pickrondes of verzamelrondes vastgelegd, dit gebeurt onder de vorm van een lijst ‘pickplaats’ met daarin de opeenvolgende pickplaatsen die moeten aangedaan worden tijdens de opeenvolgende pickrondes. Daarnaast worden ook een aantal methoden gedefinieerd die we in het hoofdprogramma gebruiken om de gegevens van de routes op te slaan. Daarmee bedoelen we dat per route tussen twee opeenvolgende pickplaatsen (of tussen een pickplaats en het CAP) wordt bijgehouden hoeveel stappen (met een stap
Hoofdstuk 4: Opbouw magazijnsimulator
70
bedoelen we de verplaatsing van het middelpunt van de ene cel naar het middelpunt van een andere cel) daarvoor nodig zijn, welke het rij- en kolomnummer en de coördinaten van de middelpunten van de opeenvolgende cellen zijn waarlangs we passeren.
8.1.1
Methoden en procedures van toepassing bij de initialisatie van het Routeobject
De
lijst
‘pickplaats’
waarin
de
pickrondes
worden
bijgehouden
is
een
vierdimensionale lijst waarbij de eerste drie dimensies respectievelijk het wavenummer, het ordernummer en het nummer van de orderregel voorstellen. De vierde dimensie bestaat uit vier niveaus : (1) x-coördinaat van de pickplaats die moet aangedaan worden om de orderregel af te handelen (2) y-coördinaat van de pickplaats die moet aangedaan worden om de orderregel af te handelen (3) rij van de pickplaats (4) kolom van de pickplaats De methoden getPickplaats en setPickplaats laten respectievelijk toe gegevens te lezen uit of te schijven in de lijst pickplaats.
De gedachte die achter de opbouw van deze lijst zit, is dat er per wave twee fictieve orders wordt ingevoegd. Eén voor de effectieve orders van de wave en één erna. Op die manier zorgen we ervoor dat iedere gesimuleerde verzamelronde start aan het depot of centraal afgiftepunt en dat ze daar ook eindigt. Dit omdat in het hoofdprogramma voor het bepalen van de startlocatie van een route gekeken wordt naar de pickplaats van de vorige orderregel (zie punt 12 Hoofdprogramma).
Om die fictieve orders aan te maken gebruiken we de twee procedures : •
Fictief_startorder (l As Integer)
Creatie van een fictief order dat voor de eigenlijke orders van de wave komt. Als argument geven we het nummer van de wave mee. Dit order krijgt het nummer 0 en
Hoofdstuk 4: Opbouw magazijnsimulator
71
heeft één orderregel waarvan de ‘pickplaats’ het CAP is en er dus niets moet gepickt worden. Een ordernummer gelijk aan 0 is de reden waarom in klasse Order, de lijst orderregels een niveau 0 heeft in de tweede dimensie. Zo kunnen we dit extra order invoegen zonder alle ordernummers te moeten aanpassen. •
Fictief_eindorder (l As Integer)
Creatie van een fictief order dat na de eigenlijke orders van de wave komt. Als argument geven we opnieuw het nummer van de wave mee. Dit order krijgt een nummer dat één hoger is dan het nummer van het laatste order van de wave. Het heeft opnieuw slechts één orderregel waarvan de pickplaats opnieuw het CAP is.
Voor het opbouwen van de lijst met pickplaatsen of dus het samenstellen van de verzamelrondes hebben we de keuze tussen twee procedures naargelang de aard van de orderspecificaties : •
Pickplaatsen_en_geen_rekken_opgegeven
Deze procedure gebruiken we in het geval dat bij de orderspecificaties niet vermeld wordt in welk rek we het te picken product kunnen vinden. De pickplaatsen worden bepaald op basis van het productnummer. Als het product dat in het rek zit overeenstemt met het product opgegeven in de orderregel dan voegen we de pickplaats die in de reklijst van het Magazijn-object bij dat specifiek rek hoort toe aan de lijst met pickplaatsen. Hierbij merken we op dat deze procedure niet in staat is om te werken in situaties waarbij éénzelfde product in meerdere rekken terug te vinden is. •
Pickplaatsen_en_rekken_opgegeven
In het geval bij de orderspecificaties het rek waaruit het product moet gepickt worden vermeld wordt kunnen we deze procedure gebruiken. De pickplaatsen worden nu bepaald op basis van het reknummer. Als het reknummer overeenkomt met het nummer opgegeven in de orderregel voegen we de pickplats die in de reklijst van het Magazijn-object bij dat rek hoort toe aan de lijst met pickplaatsen.
Hoofdstuk 4: Opbouw magazijnsimulator
72
Om de rij en kolom van de pickplaatsen te bepalen gebruiken we de procedure Pickplaatscellen.
8.1.2
Methoden enkel van toepassing in hoofdprogramma
Het betreft hier methoden die we gebruiken om gegevens te halen uit of weg te schrijven naar de lijsten startlocatie, doellocatie en route en methoden om het nummer van stap in de route en het routenummer te lezen of op te slaan.
‘Startlocatie’ is een ééndimensionale lijst waar in de verschillende niveaus de volgende gegevens worden bijgehouden over de plaats waar we ons zich bevinden bij aanvang van een orderregel: (1) de rij van de startlocatie (2) de kolom van de startlocatie (3) de x-coördinaat (4) de y-coördinaat Het lezen en het opslaan gebeurt respectievelijk met getStartlocatie en setStartlocatie. In plaats van met een lijst te werken kunnen we hier ook gebruik maken van records.
‘Doellocatie’ is analoge ééndimensionale lijst waarin dezelfde gegevens worden bijgehouden, maar dan van de doellocatie of de plaats waar we naar toe moeten voor het afwerken van een orderregel. Dit is dus de pickplaats horende bij de orderregel, wat ofwel een pickplaats is horende bij een rek ofwel het CAP. De methoden zijn getDoellocatie en setDoellocatie.
‘Route’ is een driedimensionale lijst waarbij de eerste twee dimensies respectievelijk het routenummer en het nummer van de stap in de route aangeven. In de acht niveaus van de derde dimensie worden nummers van wave, order, orderregel en stap, coördinaten en rij – en kolomnummer van de plaats waar we ons bevinden op die stap bijgehouden. De methoden zijn analoog als bij de voorgaande lijsten getRoute en setRoute.
Hoofdstuk 4: Opbouw magazijnsimulator
73
‘Aantalstappen’ is opnieuw een ééndimensionale lijst die per route bijhoudt uit hoeveel stappen de route bestaat. Op analoge manier zijn de methoden getAantalstappen en setAantalstappen.
8.2
Instellingen
De constanten die we in deze klasse moeten definiëren zijn : (1) maxwaves : het maximaal aantal waves (2) maxorders : het maximaal aantal orders per wave (3) maxorderregels : het maximaal aantal orderregels per order (4) maxroutes : het maximaal aantal routes per simulatie (5) maxstappen : het maximaal aantal stappen per route
Het instellen van de gewenste waarde gebeurt zoals besproken in hoofdstuk 3 (zie 3.1.1 Constanten)
Daarnaast moeten we één van de twee procedures waarin de lijst met pickplaatsen wordt gegenereerd activeren. Dit doen we door een ‘ te plaatsen voor de naam van de procedure die niet van toepassing is.
9. Klasse Knoop (Knoop.cls)
In deze klasse definiëren we methoden die in het hoofdprogramma gebruikt worden bij het uitvoeren van het kortste pad algoritme op een collection van knopen. Met deze methoden kunnen we informatie opvragen en wegschrijven over : (1) nummer van de knoop (getNummer en setNummer) (2) x-coördinaat van de knoop (getX en setX) (3) y-coördinaat van de knoop (getY en setY) (4) rij waarop de knoop zich bevindt (getRij en setRij) (5) kolom waarop de knoop zich bevindt (getKolom en setKolom) (6) de status van het label van de knoop (getStatus en setStatus) (permanent (2), tijdelijk (1) of geen label (0)) Hoofdstuk 4: Opbouw magazijnsimulator
74
(7) de voorgaande knoop in het korste pad (getPrevious en setPrevious) (8) de afstand van de startknoop tot deze knoop (getDistance en setDistance)
10. Klasse Dijkstra (Dijkstra.cls)
10.1
Opbouw
Bij het initialiseren van het object van deze klasse (dat we de naam ‘routingalgoritme’ geven) definiëren we alle methoden die verband houden met de uitvoering van het kortste pad algoritme. We bespreken dit in hoofdstuk 5. Het gaat om bewerkingen die uitgevoerd worden op de volgende lijsten en parameters :
‘startknopen’ is een ééndimensionale lijst waarin de nummers van de startknopen van het algoritme worden bijgehouden. Startknopen zijn knopen die rechtstreeks verbonden zijn met de startlocatie en die als beginknoop fungeren van het kortste pad algoritme, of met ander woorden de knoop van waaruit we afstand berekenen naar alle andere knopen in het netwerk. (getStartknopen en setStartknopen)
‘eindknopen’ is een ééndimensionale lijst waarin de nummers van de eindknopen van het algoritme worden bijgehouden. Eindknopen zijn knopen die rechtstreeks verbonden zijn met de doellocatie en die als eindknoop fungeren van het kortste pad algoritme, of met andere woorden die knoop waarvan we de afstand vanaf de startknoop willen kennen. (getEindknopen en setEindknopen)
‘deelafstand’ is een ééndimensionale lijst waarin de afstanden behorende tot de drie delen van een route worden bijgehouden. In hoofdstuk 5 (zie 3.4 Dijkstra’s algoritme wel van toepassing) wordt uiteen gezet hoe we de afstand van een route berekenen door ze op te splitsen in drie delen, in geval Dijkstra’s algoritme van toepassing is. (getDeelafstand en setDeelafstand)
‘afstand’ is een tweedimensionale lijst waarin voor elke combinatie van start- en eindknoop de afstand wordt bijgehouden. De eerste dimensie is de startknoop, de
Hoofdstuk 4: Opbouw magazijnsimulator
75
tweede de eindknoop. Deze lijst wordt gebruikt om de kortste route bepalen. (getAfstand en setAfstand)
‘kortstepad’ is een ééndimensionale lijst waarin de knopen die deel uitmaken van de uiteindelijk kortste route worden bijgehouden. (getKortstePad en setKortstePad)
‘aantalstart’ is het aantal startknopen, d.w.z. het aantal knopen dat rechtstreeks met de startlocatie is verbonden en als beginknoop van het kortste pad algoritme kan fungeren. (getAantalStartknopen en setAantalStartknopen)
‘aantaleind’ is het aantal eindknopen, d.w.z. het aantal knopen dat rechtstreeks met de doellocatie is verbonden en als eindknoop van het kortste pad algoritme kan fungeren. (getAantalEindknopen en setAantalEindknopen)
‘aantalknopen’ is het aantal knopen waaruit de kortste route is samengesteld. (getAantalKnopen an setAantalKnopen)
‘beginknoop’ is de startknoop van de kortste route (getBeginKnoop en setBeginKnoop)
‘eindknoop’ is de eindknoop van de kortste route (getEindKnoop en setEindKnoop)
10.2
Instellingen
Er moet één constante gedefinieerd worden nl. maxknopen, het maximaal aantal knopen in het netwerk (of in de magazijnlayout). Het instellen van de gewenste waarde gebeurt zoals aangegeven in hoofdstuk 3 (zie 3.1.1 Constanten).
11. Klasse Uitvoer (Uitvoer.cls)
Deze klasse is één van de drie plaatsen in de simulator waar interactie optreedt met Excel. Hoofdstuk 4: Opbouw magazijnsimulator
76
Eerder gebeurde dit al in de klasse Invoer en in het hoofdprogramma voor de routering.
11.1
Opbouw
Alles met betrekking tot de uitvoer van resultaten beheren we in deze klasse. Bij het aanmaken van een dergelijk object (in ons geval ‘outputdata’) met de procedure ‘init(magazijnobject As Magazijn, orderobject As Order, routeobject As route) moeten we drie argumenten meegeven, nl. objecten uit de klasse Magazijn, Order en Route. In ons geval dus ‘dc’, ‘picklijst’ en ‘weg’.
Met de procedure ‘Routering’ copiëren we de lijst route van het Route-object naar het Uitvoer-object. Deze lijst is identiek aan deze beschreven in de klasse Route. Vervolgens verschijnt er een inputbox waar de naam van het uitvoerbestand moet worden ingegeven. Deze naam wordt opgeslagen met de methode setBestandsnaam en bij het openen terug opgeroepen met de methode getBestandsnaam. Met de methode App.Path zoekt de compiler automatisch de locatie van het bestand in het geheugen. Door gebruik te maken van deze twee laatst vermelde methoden openen we het uitvoerbestand en kan de uitvoer beginnen : •
Picktijden_en_afstanden_wegschrijven
De afgelegde afstanden, picktijden en transporttijden worden tijdens de operaties in het hoofdprogramma opgeslagen in een aantal lijsten : afstandlijst, picktijdlijst, transporttijdlijst, cumulatieveafstand, cumulatievepicktijd en cumulatievetransporttijd. Dit zijn stuk voor stuk driedimensionale lijsten waarin respectievelijk de afgelegde afstand, de picktijd, de transporttijd per orderregel, de cumulatieve afstand, de cumulatieve picktijd en de cumulatieve transporttijd per wave te vinden zijn. De drie dimensies
stellen
respectievelijk
het
wavenummer,
ordernummer
en
orderregelnummer voor. Daarnaast definiëren we ook een aantal ééndimensionale lijsten : totaalafstand, totaalpicktijd en totaaltransporttijd. Hierin bevinden zich dan respectievelijk de totale afstand, de totale picktijd en de totale transporttijd per verzamelronde. Al deze resultaten worden gestructureerd weggeschreven naar het werkblad ‘tijd’ van het uitvoerbestand. Hoofdstuk 4: Opbouw magazijnsimulator
77
•
Rekken_wegschrijven
Om na het uitvoeren van de simulatie een idee te hebben van de nog voorhanden voorraad zorgen we ervoor dat de inhoud van elk rek na de simulatie in het uitvoerbestand komt te staan. Daarvoor schrijven we de reklijst (meer bepaald : reknummer, product en inhoud) van het Magazijn-object in het werkblad ‘rekken’ van de Excel-file. •
Routes_wegschrijven
De specificaties van elke route worden in het werkblad ‘routes’ van de Excel-file geschreven. Per route geven we aan : (1) het routenummer (2) welke orderregel we afwerken met de route (door aanduiden van het wavenummer, ordernummer en orderregelnummer) (3) de opeenvolgende stappen van die route( door aanduiden van het stapnummer, coördinaten van de middelpunten van de cellen en het rij- en kolomnummer van de cellen)
Voor een meer gedetailleerde beschrijving van de structuur van de ouput verwijzen we naar de beschrijving van het uitvoerbestand (zie 12. Output)
11.2
Instellingen
Opnieuw moeten we ook vijf constanten definiëren : (1) maxwaves : het maximaal aantal waves (2) maxorders : het maximaal aantal orders per wave (3) maxorderregels : het maximaal aantal orderregels per order (4) maxstappen : het maximaal aantal stappen per route (5) maxroutes : het maximaal aantal routes per simulatie
Het ingeven van de gewenste waarden gebeurt opnieuw zoals uiteengezet in hoofdstuk 3. (zie 3.1.1 Constanten)
Hoofdstuk 4: Opbouw magazijnsimulator
78
12. Hoofdprogramma (Magazijnsimulator.frm)
12.1
Opbouw
Wanneer we het programma starten, dan wordt het startformulier geladen. Hier is dat het formulier Magazijnsimulator.frm. Vanuit dit formulier wordt dan met de Showmethode het formulier Intro.frm geladen en weergegeven. Dit formulier fungeert als startscherm.
Vervolgens creëren we de objecten van de verschillende klassen en creëren we een collection van knopen, die we initialiseren met Collection_initialiseren. Het gebruik van een collection is de enige manier in Visual Basic om een dynamisch geheugen te alloceren.
De uiteindelijke simulatie wordt uitgevoerd in de procedure ‘Picken’, die is opgebouwd uit verschillende subprocedures. We bespreken nu de opbouw van deze subprocedures, waarbij we deze die betrekking hebben op het bepalen van de kortste route voorlopig buiten beschouwing laten aangezien deze in het hoofdstuk 5 behandeld worden. •
startlocatie
Bepaling van de plaats waar we ons bevinden bij aanvang van een orderregel. Deze plaats is ofwel het centraal afgiftepunt, ofwel de pickplaats van de vorige orderregel. Als het nummer van de orderregel gelijk is aan 1, dan is de vorige orderregel de laatste orderregel van het vorige order. Vandaar ook de invoering van een fictief startorder in de klasse Route. Als zowel het ordernummer als het orderregelnummer gelijk zijn aan 1, dan is de vorige orderregel regel 1 van order 0. Hier is het centraal afgiftepunt opgegeven. Op die manier zorgen we er dus voor dat de verzamelronde start bij het centraal afgiftepunt. In alle andere gevallen is de vorige orderregel de vorige regel van hetzelfde order. We schrijven de gegevens die zich over die vorige orderregel in de pickplaatslijst bevinden in de lijst startlocatie. En de cel die met de startlocatie overeenkomt krijgt ook al de waarde 3 in matrix(rij, kolom, 4), aangezien deze cel altijd tot de route behoort. Hoofdstuk 4: Opbouw magazijnsimulator
79
•
doellocatie
Bepaling van de plaats waar we naar toe moeten om de orderregel af te werken. Dit is dus hetzij de pickplaats horende bij de orderregel, hetzij het centraal afgiftepunt in het geval van afgifte. Dit is ook de reden van het invoeren van een fictief eindorder in de klasse Route. Door het invoegen van dit fictieve order (opgebouwd uit één orderregel met als specificaties het centraal afgiftepunt) na het laatste effectieve order, zorgen we ervoor dat elke pickronde eindigt met een afgifte bij dit centraal afgiftepunt. De gegevens uit de lijst pickplaats met betrekking tot de doellocatie schrijven we in de lijst doellocatie. •
Route_vastleggen
De lijst met de routegegevens aanvullen. •
Cel_zoeken (c As Integer, d As Integer)
Procedure waarmee we de kortste route cel per cel reconstrueren. Als argumenten geven we het rij- en kolomnummer mee van de plaats waar we naar toe willen. Dit kan een knoop zijn, maar ook de doellocatie. Bij de uitvoering van deze procedure vergelijken we telkens het huidige rij- en kolomnummer met de opgegeven argumenten. Op basis van deze vergelijking passen we één van de volgende subprocedures toe : Cel_rechts Cel_links Cel_onder Cel_boven Deze zorgen er respectievelijk voor dat we een cel naar rechts, links, onder of boven opschuiven. Dit gebeurt nadat we nog eens gecontroleerd (eerder hebben we al eens gecontroleerd of er zich rekken bevinden tussen de twee plaatsen, zie hoofdstuk 5) hebben of een beweging van één cel in de aangeduide zin wel mogelijk is. Dit wil zeggen dat we moeten controleren of niet tegen de rand van de magazijnlayout zitten en of de eerste volgende cel in de aangeduide zin wel deel uitmaakt van een gang. Vervolgens stellen we in elk van de procedures matrix(rij, kolom, 4) gelijk aan 3 (om aan te duiden dat deze cel deel uitmaakt van de route) , verhogen we het stapnummer met 1 en schrijven we de gegevens van de cel in de lijst ‘route’.
Hoofdstuk 4: Opbouw magazijnsimulator
80
Tot slot controleren we of de plaats waar we naartoe willen (knoop of doellocatie) reeds bereikt is. In dat geval geven we de variabele reachcheck de waarde true. •
Kortste_pad_zonder_Dijkstra_enkele_gang
We bepalen hier de route en de afstand van de route indien de start- en doellocatie in dezelfde enkele pickgang liggen. Eerst bepalen we de afstand tussen de start- en doellocatie en schrijven die in de afstandlijst. De afstand berekenen we dooor het verschil te berekenen tussen de xcoördinaten of de y-coördinaten afhankelijk of de beide locaties dezelfde y- of xcoördinaat hebben. Daarna stellen we het nummer van de stap gelijk aan 0, het rijnummer gelijk aan dat van de startlocatie en het kolomnummer ook gelijk aan dat van de startlocatie. Deze gegevens schrijven we dan in de lijst route met de procedure Route_vastleggen. Vervolgens controleren we of start- en doellocatie niet samenvallen (op basis van coördinaten). Dit duiden we aan door de Boolse variabele reachcheck te gerbuiken, die de waarde false krijgt als de doellocatie nog niet bereikt is, en de waarde true indien dit wel het geval is. Zolang de variabele reachcheck de waarde false heeft herhalen we de procedure Cel_zoeken waarbij we als argumenten het rij- en kolomnummer van de doellocatie meegeven. •
Kortste_pad_zonder_Dijkstra_dubbele_gang
Deze procedure laat toe om de afstand en de route te bepalen in het geval de start- en doellocatie in dezelfde dubbele pickgang liggen. Het principe is analoog aan dat van de procedure in het geval van een enkele gang, maar toch zijn er twee verschillen. Eerst en vooral stellen we hier het nummer van de stap in het begin van de procedure gelijk aan 1. Dit omdat we reeds een cel verschoven zijn in het rooster (zie hoofdstuk 5). Een tweede verschil is dat de afstand niet gelijk is aan enkel een verschil van x- of ycoördinaten, maar dat we bij dat verschil nog eens de lengte van een celzijde moeten optellen. Opnieuw omwille van het feit dat we reeds een cel zijn opgeschoven in het rooster.
Hoofdstuk 4: Opbouw magazijnsimulator
81
•
Transporttijd_en_picktijd
De berekening van de transporttijd per orderregel is steeds de afglegde afstand gedeeld door de snelheid van het transportmiddel. Deze waarde schrijven we daarna in de lijst ‘transporttijdlijst’. Bij de berekening van de picktijd moeten we eerst controleren of het om het laatste order van een verzamelronde gaat. Is dat het geval dan is de picktijd gelijk aan de transporttijd. Is dat niet het geval dan moeten we bij de transporttijd nog de gemiddelde picktijd optellen. Deze waarde schrijven we dan in de picktijdlijst. •
Totale_transporttijd_picktijd_en_afstand
De totaal afgelegde afstand van een wave vermeerderen we telkens met de waarde uit de afstandlijst die met de orderregel in kwestie overeenstemt. Deze totale afstand schrijven we dan op de daartoe bestemde plaats in de lijst ‘cumulatieveafstand’. We hanteren hetzelfde principe voor wat betreft de transporttijd en de picktijd.
Zoals reeds eerder in dit hoofdstuk vermeld, verschijnt er op het scherm een grid of rooster tijdens de simulatie. De cellen van deze grid hebben een bepaalde kleur die met een bepaalde toestand overeenstemt. In welke kleur de cellen worden afgebeeld, wordt bepaalde door de waarde van matrix(rij, kolom, 4) (zie 6. Klasse Magazijn). We overlopen nu de procedures van het hoofdprogramma die tot doel hebben om de cellen in de gewenste kleur af te beelden. Daarvoor controleren ze eerst welke ‘toestand’ de cel heeft en daarna zorgen ze ervoor dat matrix(rij, kolom, 4) de gepaste waarde heeft. •
Rekken_en_pickplaatsen
Per verzamelronde geven we rekken waaruit moet gepickt worden een groene kleur en de bijhorende pickplaatsen een lichtblauwe kleur. Voor de pickplaatsen zijn dit de cellen van de grid met hetzelfde rij- en kolomnummer als één van de plaatsen uit de pickplaatslijst. (matrix(rij, kolom, 4) = 5) Voor de rekken controleren we eerst of het reknummer overeenkomt met het reknummer vermeld in de orderregel. Is deze voorwaarde voldaan dan controleren welke cel dezelfde x- en y-coördinaten heeft als dat rek. (matrix(rij, kolom, 4) = 4)
Hoofdstuk 4: Opbouw magazijnsimulator
82
•
Rek_na_picken
Hier controleren we eerst of het nummer van een rek overeenkomt met het reknummer vermeld op de orderlijst. Eens bepaald uit welk rek gepickt werd verminderen we inhoud van dat rek met het aantal eenheden vermeld op de orderregel. Om daarna de cel die met dat rek overeenkomt een donkerblauwe kleur te geven bepalen we welke cel hetzelfde rij- en kolomnummer heeft als het rek. (matrix(rij, kolom, 4) = 6) •
Meerdere_keren_picken_uit_een_rek
Er bestaat een mogelijkheid dat tijdens één en dezelfde pickronde meerder malen uit éénzelfde rek producten moeten verzameld worden. We gebruiken dan deze procedure om er voor te zorgen dat zo’n rek (dat normaal, nadat er een eerste keer producten werden uitgenomen, donkerblauw wordt afgebeeld) opnieuw groen op het scherm wordt getoond. Dit gebeurt eigenlijk volgens het zelfde principe als bij de procedure hierboven. Eerst de rekken bepalen waaruit nog moet gepickt worden gedurende deze pickronde en dan op basis van het rij- en kolomnummer de overeenkomstige cel bepalen. (matrix(rij, kolom, 4) = 4) •
Layout_initialiseren
Procedure om de layout van het magazijn weer te geven zonder knopen. Die layout wordt eerst getoond met aanduiding van de knopen (gele punten). Omdat er situaties denkbaar zijn dat er knopen samenvallen met bijvoorbeeld het CAP, initialiseren we daarna de layout, zodat we de echte layout van het magazijn kunnen zien. Daarmee bedoelen we dat plaatsen waar zich knopen bevinden opnieuw als deel van een gang of CAP worden weergegeven. Dit bepalen we opnieuw op basis van het rij- en kolomnummer. •
Pad_initialiseren
Met deze procedure zorgen we ervoor dat, nadat een orderregel is afgewerkt en de bijhorende route op het scherm is weergegeven, de cellen die deel uitmaakten van de route opnieuw als deel van een gang worden weergegeven. Daarbij controleren we eerst of de cel geen pickplaats is die nog moet aangedaan worden tijdens deze verzamelronde.
Hoofdstuk 4: Opbouw magazijnsimulator
83
•
Startknopen_en_eindknopen_en_kortstepad_initialiseren
Procedure om deze lijsten te initialiseren, d.w.z. dat we alle waarden in deze lijsten gelijk aan 0 stellen. •
Form_paint
Deze procedure zorgt er uiteindelijk voor dat de grid op het scherm verschijnt. Hier tekenen we de verschillende rechthoekjes met de gewenste kleur op het scherm met behulp van de grafische methode die in hoofdstuk 3 werd aangehaald (zie 5. Grafische methoden in VB).
12.2
Instellingen
De enige constante die we moeten definiëren is maxknopen, het maximaal aantal knopen.
13. Output
De outputfile is een Excel-file opgebouwd uit drie sheets of werkbladen : 1. Tijd 2. Rekken 3. Routes
13.1
tijd
In het werkblad ‘tijd’ geven we aan één cel de naam ‘ tijden’. Na de simulatie verschijnen in dit werkblad de afstanden, picktijden en transporttijden per orderregel en per wave.
In de eerste cel van het werkblad schrijven we de titel van de sheet, ‘Tijden en afstanden’. Verder voorzien we negen kolommen van een naam:
Hoofdstuk 4: Opbouw magazijnsimulator
84
wave : kolom waarin het wavenummer wordt weergegeven, dit gebeurt per wave maar één maal order : kolom waarin het ordernummer wordt weergegeven, dit gebeurt per order ook maar één maal orderregel : kolom waarin het orderregelnummer wordt weergegeven afstand orderregel : kolom waarin de afstand wordt weergegeven die wordt afgelegd om de orderregel af te handelen, uitgedrukt in meter cumulatieve afstand : kolom waarin de cumulatieve afgelegde afstand van de wave waartoe de orderregel behoort wordt weergegeven, eveneens uitgedrukt in meter transporttijd orderregel : kolom waarin de tijd wordt weergegeven die nodig is om de afstand uit kolom ‘afstand orderregel’ af te leggen, deze waarde is dus gelijk aan de afstand gedeeld door de snelheid van het intern transportmaterieel, uitgedrukt in seconden cumulatieve transporttijd : kolom waarin de som van transporttijden van alle orderregels die tot en met de huidige zijn afgewerkt en behoren tot dezelfde wave wordt weergegeven, eveneens uitgedrukt in seconden picktijd orderregel : kolom waarin de totale tijd nodig om de orderregel af te werken, wordt weergegeven, deze tijd is dus de som van de transporttijd en de gemiddelde picktijd opgegeven in het invoerbestand, uitgedrukt in seconden cumulatieve picktijd : kolom waarin de som van picktijden van alle orderregels die tot en met de huidige zijn afgewerkt en behoren tot dezelfde wave wordt weergegeven, eveneens uitgedrukt in seconden
13.2
rekken
In het werkblad ‘rekken’ geven we aan één cel de naam ‘ rekken’. Na de simulatie verschijnt in dit werkblad de inhoud van de verschillende rekken.
In de eerste cel schrijven we de titel van de sheet, ‘Rekken’. Verder voorzien we drie kolommen van een naam:
Hoofdstuk 4: Opbouw magazijnsimulator
85
reknummer : kolom waarin het reknummer wordt weergegeven product : kolom waarin het nummer van het product in het rek wordt weergegeven aantal eenheden : kolom waarin het aantal SKU’s (stock keeping units) of eenheden van het product in het rek wordt weergegeven
13.3
routes
In het werkblad ‘routes’geven we aan één cel de naam ‘route’. Na de simulatie verschijnen in dit werkblad de data van de verschillende routes, met name het routenummer, specificatie van wave, order en orderregel en de verschillende stappen met aanduiding van de x- en y- coördinaten en rij- en kolomnummer van de cellen.
In de eerste cel schrijven we de titel van de sheet, ‘Route’. Verder voorzien we negen kolommen van een naam: route : kolom waarin het nummer van de route verschijnt, dit gebeurt één maal per route wave : kolom waarin het wavenummer waartoe de orderregel behoort verschijnt, ook een éénmalige aanduiding order : analoog als bij wave, maar voor het ordernummer, ook éénmalige aanduiding regel : kolom waarin het nummer van de orderregel verschijnt die we afhandelen met de desbetreffende route stap : kolom waarin het nummer van de stap wordt weergegeven x : kolom waarin de x-coördinaat van de cel die tijdens de betreffende stap wordt aangedaan verschijnt y : analoog voor de y-coördinaat rij : analoog voor het rijnummer kolom : analoog voor het kolomnummer
Hoofdstuk 4: Opbouw magazijnsimulator
86
Hoofdstuk 5 :
Routeringsalgoritme
1. Inleiding
Een automobilist wil bijvoorbeeld de kortste weg weten van Oostende naar Brussel. Gegeven daarbij is wegennet van België waarbij de afstand tussen ieder tweetal kruisingen bekend is. Hoe moeten we dan te werk gaan om de kortste route te bepalen ? Een mogelijkheid zou kunnen zijn alle mogelijke routes op te sommen en per route de afstand te bepalen, om dan vervolgens de kortste route te kiezen. Het is echter duidelijk dat we op die manier vele routes bekijken die niet interessant zijn.
Dit probleem wordt in de literatuur omschreven als het kortste pad probleem. Vertaald naar de problematiek van de magazijnsimulator kan dit als volgt omschreven worden : gegeven twee opeenvolgende pickplaatsen en de magazijnlayout met het netwerk van knopen, moet de magazijnier of orderpicker het kortste pad of de kortste route zoeken tussen deze twee punten. We onderscheiden twee soorten methoden om het kortste pad probleem op te lossen : label-setting methoden en labelcorrecting-methoden. Elk van deze methoden bepaalt de kortste paden van een bronknoop naar alle andere knopen, is iteratief en maakt gebruik van afstandslabels. De methoden verschillen in de manier waarop ze de labels toekennen. Een label-setting algoritme maakt in elke iteratie één label permanent, dat wil zeggen dat het label het werkelijke kortste pad bevat.
Een label-correcting
algoritme beschouwt alle labels als tijdelijk, tot aan de laatste iteratie, wanneer alle labels tegelijkertijd permanent worden.
Hoofdstuk 5: Routeringsalgoritme
87
2. Het kortste pad algoritme van Dijkstra
2.1
Algemeen
Dijkstra’s algoritme is een voorbeeld van een label-setting methode. Het wijst tijdelijke labels toe aan de knopen en maakt vervolgens per iteratie het label van één knoop permanent. Dit label bevat de afstand van het kortste pad vanaf de bronknoop tot deze knoop en bevat tevens de voorgaande knoop, dit om nadien het pad te kunnen reconstrueren. Het algoritme vindt dus de kortste paden van een bronknoop naar alle overige knopen in een graaf met n knopen. Een graaf bestaat uit een verzameling van knopen en een verzameling van paren van verschillende knopen (takken).
2.2
Algoritme
We bespreken nu de verschillende stappen in het algoritme van Dijkstra waarbij we een permanent label aanduiden met [ , ] en een tijdelijk label met ( , ). We illustreren dit aan de hand van een voorbeeld vergelijkbaar met de situatie in een magazijn.
Figuur 5.1
Voorbeeld van een knopennetwerk
Hoofdstuk 5: Routeringsalgoritme
88
Stap 1 :
We geven aan de startknoop het permanente label [0,S]. Het eerste cijfer is de afstand van de startknoop tot deze knoop (dus gelijk aan 0). Het tweede cijfer is het nummer van de voorgaande knoop. Aangezien de startknoop geen voorgaande knopen heeft, is dit dus de startknoop zelf.
Stap 2 :
We geven nu aan elke knoop die rechtstreeks vanuit de startknoop kan bereikt worden een tijdelijk label (d,S). Het eerste cijfer is de afstand van de startknoop tot deze knoop. Het tweede cijfer is het nummer van de voorgaande knoop, in dit geval de startknoop.
Figuur 5.2
Stap 3 :
Labels na stap 2
We beschouwen nu alle knopen met een tijdelijk label en geven de knoop met de kleinste waarde voor de afstand een permanent label. Als alle knopen een permanent label hebben gaan we naar de laatste stap.
Stap 4 :
We vertrekken nu van de knoop (n) die we in de vorige stap een permanent label hebben gegeven. We berekenen dan voor elke knoop m die rechtstreeks vanuit de knoop n kan bereikt worden de waarde t =
Hoofdstuk 5: Routeringsalgoritme
89
afstand van de startknoop tot knoop n + afstand van knoop n tot knoop m. Er zijn twee mogelijkheden : 1) knoop m heeft nog geen tijdelijk label; in dit geval creëren we een tijdelijk label (t,n) 2) knoop m heeft al een tijdelijk label; in dit geval zullen we het bestaande label vervangen als en slechts als de waarde t kleiner is dan de huidige waarde voor de afstand vanaf de startknoop, in het het geval t groter dan of gelijk is aan deze waarde behouden we het reeds bestaande label. In elk van de gevallen keren we terug naar stap3.
Figuur 5.3
Stap 5 :
Labels na stap 4
Alle knopen hebben een permanent label dat de kortste afstand weergeeft van de startknoop tot de knoop in kwestie en tevens de voorgaande knoop in het kortste pad weergeeft. Het kortste pad kan nu gereconstrueerd worden door achterwaarts te redeneren, vertrekkende van een gegeven knoop steeds naar de voorgaande knoop gaan tot de startknoop bereikt wordt.
Hoofdstuk 5: Routeringsalgoritme
90
Figuur 5.4
Labels na beëindigen algoritme
3. Implementatie routeringsalgoritme in magazijnsimulator
3.1
Inleiding
Bij het implementeren van het routeringsalgoritme in de magazijnsimulator moeten we drie gevallen onderscheiden:
1) de twee opeenvolgende pickplaatsen bevinden zich in dezelfde enkele pickgang 2) de twee opeenvolgende pickplaatsen bevinden zich in dezelfde dubbele pickgang 3) de twee opeenvolgende pickplaatsen bevinden zich niet in dezelfde pickgang, we moeten dus van gang wisselen
Voor ieder van deze gevallen bepalen we het kortste pad op een andere manier. In het eerste geval bepalen we de rechtlijnige weg tussen de twee punten. In het tweede geval zorgen we er eerst voor dat we ons op dezelfde lijn van de doellocatie bevinden door een rij naar onder of boven of een kolom naar links of rechts te bewegen en
Hoofdstuk 5: Routeringsalgoritme
91
bepalen we vervolgens de rechtlijnige weg tussen die twee punten. In het derde geval passen we het kortste pad algoritme van Dijkstra toe gecombineerd met het bepalen van het rechtlijnige pad tussen startlocatie en de beginknoop van het algoritme en tussen de eindknoop van het algoritme en de doellocatie.
Figuur 5.5
Figuur 5.6
Hoofdstuk 5: Routeringsalgoritme
Start- en doellocatie in dezelfde enkele pickgang
Start- en doellocatie in dezelfde dubbele pickgang
92
Figuur 5.7
3.2
Start- en doellocatie niet in dezelfde pickgang
Controle of Dijkstra’s algoritme van toepassing is
De eerste vraag die moet beantwoord worden als we kortste route willen bepalen is of het wel noodzakelijk is Dijkstra’s algoritme toe te passen ? Indien de start- en doellocatie in dezelfde enkele of dubbele pickgang liggen dan bestaat de kans dat we met Dijkstra’s algoritme niet dé kortste route vinden. Immers aangezien er zich geen knopen bevinden in de pickgangen en het kortste pad algoritme gebruik maakt van deze knopen, zouden we dan in principe steeds ons eerst buiten de pickgang moeten begeven en daarna terug de pickgang moeten binnen gaan. Het spreekt voor zich dat dit bezwaarlijk een realistische situatie kan genoemd worden.
3.2.1
Controle of start- en doellocatie in dezelfde enkele pickgang liggen
De procedure ‘Dijkstra_check’ laat toe om te controleren of de start- en doellocatie al dan niet in dezelfde enkele pickgang bevinden. Voor we verder de werking van deze procedure toelichten, leggen we eerst het principe uit van vier subprocedures die gebruikt worden in Dijkstra_check :
Hoofdstuk 5: Routeringsalgoritme
93
•
Controle_knopen_in_de_Xrichting (target As Double)
Het doel van deze procedure is :
-
hetzij te controleren of een knoop die dezelfde y-coördinaat heeft als de start- of doellocatie wel rechtstreeks verbonden is met deze locatie, of anders geformuleerd als er zich nog knopen bevinden tussen de locatie en de knoop
-
hetzij te controleren of er zich tussen de start- en doellocatie nog knopen bevinden, in het geval dat de beide locaties dezelfde y-coördinaat hebben
Hier gebruiken we de procedure om het tweede geval te controleren en geven we als argument de x-coördinaat van de doellocatie mee (weg.getDoelLocatie(3)). Vertrekkende van de x- en y-coördinaten van de startlocatie verplaatsen we ons in het rooster cel per cel in de x-richting tot we de cel naast de doellocatie bereiken, dit is de cel links ervan als we naar rechts bewegen en de cel rechts ervan als we naar links bewegen. Hierbij controleren we telkens of de coördinaten van de cel overeenkomen met die van één van de knopen. De zin van de beweging wordt bepaald door het teken van het verschil in xcoördinaten van de doel - en startlocatie. Negatief impliceert een beweging naar links en positief een beweging naar rechts. We gebruiken een Boolse variabele closestcheck die al dan niet de waarde false krijgt, naargelang er al dan niet knopen worden gevonden die aan de gestelde voorwaarden voldoen. •
Controle_knopen_in_Yrichting (target As Double)
Analoge procedure als de voorgaande maar dan voor locaties of knopen en locaties met dezelfde x-coördinaat. Ditmaal geven we als argument de y-coördinaat van de doellocatie mee (weg.getDoelLocatie(4)). •
Controle_op_rekken_in_Xrichting (target As Double)
Het doel van deze procedure is :
Hoofdstuk 5: Routeringsalgoritme
94
-
hetzij te controleren of er zich op de rechte lijn tussen de start- of doellocatie en de dichtstbijgelegen knoop geen rekken bevinden, indien ze dezelfde y-coördinaat hebben
-
hetzij te controleren of er zich op de rechte lijn tussen de start- en de doellocatie geen rekken bevinden, indien ze dezelfde y-coördinaat
Hier gebruiken we de procedure opnieuw om het tweede geval te controleren en geven we als argument de kolom van de de doellocatie mee (weg.getDoelLocatie(2)). Vertrekkende van de rij en kolom van de startlocatie verplaatsen we ons cel per cel in het rooster tot we de doellocatie bereiken en controleren of de cel in kwestie een rek is. Dit doen we door de coördinaten van het middelpunt van de cel te vergelijken met die van de verschillende rekken. We verplaatsen ons naar links of naar rechts naargelang het kolomnummer van de startlocatie groter of kleiner is dan die van de doellocatie. We gebruiken de Boolse variabele aislecheck die al dan niet de waarde false krijgt naargelang er zich al dan niet rekken bevinden op de rechte lijn tussen de twee punten. •
Controle_op_rekken_in_Yrichting (target As Double)
Analoge procedure als de voorgaande maar dan voor locaties of knopen en locaties met dezelfde x-coördinaat. Ditmaal geven we als argument de rij van de doellocatie mee (weg.getDoelLocatie(1)).
De werking van Dijkstra_check is dan vrij eenvoudig uit te leggen. De variabele dijkstracheck krijgt initieel de waarde true toegewezen. Als de x-coördinaten van start- en doellocatie gelijk zijn, worden controles uitgevoerd op knopen en rekken in de y-richting, pas als alle drie de voorwaarden vervuld zijn krijgt de variabele dijkstracheck de waarde false. Als de y-coördinaten van de start- en doellocatie gelijk zijn, worden controles uitgevoerd op knopen en rekken in de x-richting, pas als alle drie de voorwaarden vervuld zijn krijgt de variabele dijkstracheck de waarde false.
Hoofdstuk 5: Routeringsalgoritme
95
3.2.2
Controle of start- en doellocatie in dezelfde dubbele pickgang liggen
Naast het controleren of start- en doellocatie in dezelfde enkele pickgang liggen moeten we ook nog een andere mogelijkheid controleren. Namelijk of de start- en doellocatie in dezelfde dubbele pickgang liggen. Dit doen we met de procedure Dubbele_gang_check. Daarbij kunnen we vier gevallen onderscheiden : de doellocatie bevindt zich in de kolom rechts van de startlocatie de doellocatie bevindt zich in de kolom links van de startlocatie de doellocatie bevindt zich in de rij boven de startlocatie de doellocatie bevindt zich in de rij onder de startlocatie
In elk van deze gevallen verplaatsen we ons indien mogelijk (d.w.z. indien de betreffende cel van het rooster deel uitmaakt van een gang) één cel zodat we ons op een rechte lijn met de doellocatie bevinden. Deze cel beschouwen we dan bij de verdere controles als de ‘nieuwe’ startlocatie. De oorspronkelijke startlocatie schrijven we dan al weg naar de lijst route. Vervolgens controleren we of er zich geen knopen of rekken bevinden tussen de nieuwe startlocatie en de doellocatie. Dit wordt dan weergegeven door de variabele doubleaislecheck die de waarde true krijgt indien er zich geen rekken en knopen bevinden tussen de twee vermelde punten. Indien één van deze twee voorwaarden niet vervuld is, krijgt doubleaislecheck de waarde false.
3.3
Dijkstra’s algoritme niet van toepassing
Indien na de controle Dijkstra_check en Dubbele_gang_check de variabelen dijkstracheck en doubelaislecheck de waarde false heeft, dan kunnen we het kortste pad tussen de start- en doellocatie bepalen zonder Dijkstra’s algoritme te moeten toepassen aangezien de beide locaties in de zelfde enkele pickgang liggen. Het is in dit geval gewoon een rechte lijn tussen de twee punten. De reconstructie van de route en
het
wegschrijven
van
de
Hoofdstuk 5: Routeringsalgoritme
route
gebeuren
dan
in
de
procedure
96
Kortste_pad_zonder_Dijkstra_enkele_gang
die
in
hoofdstuk
4
(zie
12.
Hoofdprogramma) aan bod is gekomen.
Indien de waarde van doubleaislecheck true is, dan gebruiken we de procedure Kortste_pad_zonder_Dijkstra_dubbele_gang om het pad tussen de ‘nieuwe’ startlocatie en doellocatie te reconstrueren. Voor nadere uitleg over deze procedure verwijzen we naar hoofdstuk 4 (zie 12. Hoofdprogramma).
3.4
Dijkstra’s algoritme wel van toepassing
Indien we het kortste pad wel moeten bepalen door toepassing van Dijkstra’s algoritme (doubelaislecheck = false, dijkstracheck = true) dan doen we beroep op de procedure Kortste_pad_met_Dijkstra.
Samengevat voert deze procedure de volgende stappen uit : 1. bepalen van knopen die rechtstreeks verbonden zijn met startlocatie (startknopen) 2. bepalen van knopen die rechtstreeks verbonden zijn met doellocatie (eindknopen) 3. voor elke startknoop het kortste pad algoritme toepassen 4. voor elke combinatie van start- en eindknopen de lengte bepalen van de route, waarbij de route
wordt opgesplitst in drie delen : starlocatie-startknoop,
startknoop-eindknoop, eindknoop-doellocatie 5. uit alle mogelijke routes degene halen met de kortste afstand 6. route reconstrueren
Deze procedure is opgebouwd uit subprocedures waarvan we hier deze toelichten die betrekking hebben op de routing. •
Startknopen_Dijkstra
We bepalen de knopen die als startknopen kunnen fungeren voor het algoritme. Daarmee bedoelen elke knoop die rechtstreeks verbonden is met de startlocatie. Dit kunnen er dus maximaal vier zijn, twee in de x-richting en twee in de y-richting. Voor elke knoop controleren we of hetzij de x-coördinaat, hetzij de y-coördinaat overeenkomt met die van de startlocatie. Als aan één van deze voorwaarden voldaan Hoofdstuk 5: Routeringsalgoritme
97
is, controleren we of deze knoop wel rechtstreeks verbonden is met de startlocatie en of er zich geen rekken bevinden tussen deze twee punten. Daartoe maken gebruik van de hierboven vermelde procedures (met vermelding van het argument) Controle_knopen_in_Xrichting (mag.getcheckpointlijst(m,1)) argument = x-coördinaat knoop Controle_knopen_in_Yrichting (mag.getcheckpointlijst(m,2)) argument = y-coördinaat knoop Controle_op_rekken_in_Xrichting (mag.getcheckpointlijst(m,4)) argument = kolom knoop Controle_op_rekken_in_Yrichting (mag.getcheckpointlijst(m,3)) argument = rij knoop Het werkingsprincipe is hetzelfde zoals hierboven uiteen gezet.
Als aan drie opeenvolgende voorwaarden voldaan is, voegen we de knoop in kwestie toe aan de lijst met startknopen. Als alle knopen gecontroleerd zijn leggen we ook het aantal startknopen vast. Dit gebeurt met methoden van het Dijkstra-object. •
Eindknopen_Dijkstra
De werkwijze is hier identiek aan deze van Startknopen_Dijkstra, maar nu zoeken we de knopen die als eindknoop van het kortste pad algoritme fungeren en zoeken we dus de knopen die rechtstreeks verbonden zijn met de doellocatie. Deze knopen worden toegevoegd aan de lijst met eindknopen. Als alle knopen gecontroleerd zijn leggen we het aantal eindknopen vast. Opnieuw maken we hierbij gebruik van methoden van het Dijkstra-object.
Hoofdstuk 5: Routeringsalgoritme
98
Figuur 5.8 •
Bepaling start- en eindknopen
Dijkstra (i As Integer)
We passen het kortste pad algoritme toe, waarbij het nummer van de startknoop als argument wordt meegegeven. Hierbij doorlopen we de volgende stappen : First_iteration (i As Integer) In de eerste stap leggen we eerst de eigenschappen van de startknoop vast. De startknoop krijgt een permanent label (status = 2), de vorige knoop is de startknoop zelf en afstand vanaf de startknoop is 0. Vervolgens zoeken we in het netwerkrooster (zie 3.5 Netwerk, hoofdstuk 4) naar de knopen die rechtstreeks verbonden zijn met de startknoop. Deze knopen geven we een tijdelijk label, de vorige knoop is de startknoop en de afstand vanaf de startknoop is gelijk aan de waarde die in het rooster met verbindingen staat opgegeven. Next_node We bepalen de volgende knoop die een permanent label krijgt of met andere woorden de knoop van waaruit we vertrekken in de volgende
Hoofdstuk 5: Routeringsalgoritme
99
iteratie. Daarvoor overlopen we alle knopen met een tijdelijk label (status = 1) en halen er deze uit met de kleinste distance-waarde (kleinste afstand vanaf de startknoop). Deze krijgt dan een permanent label (status = 2). De variabele nextnode bevat het nummer van de volgende knoop. Check_permanent_label Aangezien het algoritme stopt van zodra alle knopen een permanent label hebben, moeten we na iedere stap controleren of deze voorwaarde al dan niet voldaan is. Dit doen we door na iedere stap het aantal knopen met een permanent label te tellen. Indien dit aantal overeenkomt met het aantal knopen in de collection dan hebben alle knopen een permanent label en mag het algoritme gestopt worden. Dit geven we aan door de Boolse variabele labelcheck de waarde true te geven. Next_iteration (i As Integer) Een volgende iteratie van het kortste pad algoritme. Hier vertrekken we van de knoop die als laatste een permanent label heeft gekregen, dus deze waarvan het nummer wordt gegeven door nextnode. Dit geven we dan ook mee als argument. Vertrekkende van deze knoop (n) zoeken we in het rooster met rechtstreekse verbindingen de knopen (m) die er rechtstreeks mee verbonden zijn. Wanneer deze laatste (m) nog geen label hebben (status = 0) dan geven we ze een tijdelijk label (status = 1), de vorige knoop is dan nextnode en de afstand is dan gelijk aan t waarbij t = afstand van de startknoop tot knoop n + afstand van knoop n tot knoop m. Wanneer de knopen (m) een tijdelijk label hebben dan zullen we dit label slechts aanpassen als t < distance-waarde (m), waarbij t = afstand van de startknoop tot knoop n + afstand van knoop n tot knoop m. Indien deze voorwaarde voldaan is blijft het label wel tijdelijk maar de vorige knoop wordt nu knoop n (nextnode) en de afstand vanaf de startknoop wordt gelijk gesteld aan de waarde t.
Hoofdstuk 5: Routeringsalgoritme
100
Deze laatste drie stappen blijven we herhalen tot alle knopen een permanent label hebben. •
Scheme
Met deze procedure schrijven we de resultaten van Dijkstra’s algoritme weg naar een Excel-worksheet (zie 3.6 Schema, hoofdstuk 4), zodat we uit dat rooster het kortste pad kunnen reconstrueren. Wanner dit meerdere malen moet gebeuren (maximum is vier keer overeenkomstig het maximaal aantal startknopen) zorgen we ervoor dat daarvoor telkens een nieuwe worksheet voor gebruikt wordt. •
Collection_initialiseren
Hier stellen we de begineigenschappen van elke knoop opnieuw in. Dit doen we telkens we Dijkstra’s algoritme willen uitvoeren. •
Kortste_route
Om uiteindelijk de kortste route te bepalen tussen de startlocatie en de doellocatie passen we deze procedure toe. Voor elk startknoop bepalen we met Dijkstra’s algoritme de kortste afstand naar alle andere knopen. Vervolgens berekenen we voor elke combinatie van één bepaalde startknoop met één van de eindknopen de lengte van de route vanaf de startlocatie tot de doellocatie. Daarvoor splitsen we de route op in drie delen : (1) een deel van de startlocatie tot de startknoop : deze afstand berekenen we met Afstand_tussen_startlocatie_en_startknoop op basis van het verschil van de x- of y-coördinaten, indien de beide punten respectievelijk dezelfde x- of y-coördinaat hebben (2) een deel van de startknoop tot de eindknoop : deze afstand bekomen we door in het rooster met de resultaten van het kortste pad algoritme op de rij die overeenkomt met de eindknoop de waarde in de kolom ‘distance’ te nemen (3) een deel van de eindknoop tot de doellocatie : deze afstand berekenen we met Afstand_tussen_doellocatie_en_eindknoop opnieuw op basis van het verschil van de x- of y-coördinaten, opnieuw indien de beide punten respectievelijk dezelfde x- en y-coördinaten hebben
Hoofdstuk 5: Routeringsalgoritme
101
Elk van deze delen worden opgeslagen in één van de drie niveaus van de ééndimensionale lijst ‘deelafstand’ van het Dijkstra-object. De som van de drie afstanden slaan we op in de lijst ‘afstand’ van het Dijkstra-object.
Figuur 5.9
De drie delen van een route
Uit die lijst halen we dan de combinatie van start- en eindknoop corresponderend met de kortste route. Dit doen we met Bepaling_kortste_route. De nummers van de startknoop en eindknoop worden respectievelijk gegeven door de variabelen start en eind. Deze stellen we initieel gelijk aan de nummers van de knopen van de eerste combinatie. Daarna overlopen we alle combinaties. Telkens we een combinatie vinden waarvan de afstand van de route kleiner is dan de huidige beste combinatie, veranderen we de waarde van de variabelen start en eind en dus van de variabelen beginknoop en eindknoop.
Op basis van deze variabelen stellen we de volledige route samen door terug te keren naar de schema’s met de resultaten van de verschillende keren dat we het kortste pad algoritme
hebben
toegepast.
Daarvoor
gebruiken
we
de
procedure
Knopen_kortste_route. We gaan naar de worksheet die de resultaten bevat van Dijkstra’s algoritme toegepast op de startknoop (start) van de kortste route en vertrekken dan van de rij waarop de eindknoop van de kortste route (eindknoop) staat weergegeven. We gaan dan telkens naar de rij die overeenkomt met de knoop
Hoofdstuk 5: Routeringsalgoritme
102
opgegeven in de kolom Previous (vorige knoop) tot we terug bij de startknoop aanbelanden. Alle knopen die we gedurende die sequentie hebben aangedaan behoren tot de korste route.
Daarna moeten we de route nog opslaan en afbeelden op het scherm. Dit gebeurt met procedures waar in hoofdstuk 4 aandacht aan werd besteed.
Hoofdstuk 5: Routeringsalgoritme
103
Hoofdstuk 6 :
Simulaties
1. Inleiding
De structuur van het invoerbestand hebben we reeds besproken in hoofdstuk 4. De bedoeling van dit hoofdstuk is om eerst wat dieper in te gaan op de invulling van de verschillende parameters die in het invoerbestand voorkomen. Vervolgens beschrijven we het verloop van een simulatie met de magazijnsimulator beschreven in hoofdstuk 4. Tenslotte bespreken we kort de uitgevoerde simulaties.
2. Parameters in het invoerbestand
2.1
Layoutparameters
Bij het ingeven van breedte en diepte van het magazijn en lengte van de celzijde moeten we er eerst en vooral op letten dat de deling van respectievelijk breedte en diepte door de celzijde een geheel getal oplevert. Deze delingen komen immers respectievelijk overeen met het aantal kolommen en het aantal rijen.
Aangezien de lengte van de celzijde de breedte van de gangen bepaald (zowel enkele als dubbele) moeten we er ook voor zorgen dat deze lengte een realistische simulatieomgeving oplevert. Dit wordt bepaald door het transportmiddel dat in het magazijn gebruikt wordt. Ieder transportmiddel heeft een zekere gangpadbreedte nodig om zich goed te kunnen verplaatsen binnen het magazijn. Onderstaande tabel geeft een overzicht van de noodzakelijke breedte van het gangpad voor de verschillende transportmiddelen .
Hoofdstuk 6: Simulaties
104
Tabel 6.1
Nodige gangpadbreedte voor het gebruik van een transportmiddel
Transportmiddel
Gangpadbreedte (mm)
2.2
Vorkheftruck
3000-4000
Reachtruck
2300-2700
Pallettruck
1600-3000
Handpallettruck
1450-2000
Stapelaar
1700-2500
Snelheidsparameter
Deze wordt opnieuw bepaald door het gebruikte transportmiddel. Daarvoor verwijzen we naar tabel 2.2 in hoofdstuk2.
2.3
Picktijdparameter
Indien het picken manueel gebeurt moeten we een onderscheid maken tussen met papier en papierloos picken. Een tabel met het verschil in picktijd hebben we reeds gegeven in hoofdstuk 2 (tabel 2.3). Gebeurt het picken met het transportmiddel dan wordt de gemiddelde picktijd in hoofdzaak bepaald door de hef- en daalsnelheid van het gebruikte transportmiddel maar ook door de gemiddelde hoogte van de stellingen.
We trachten dit te verduidelijken aan de hand van een voorbeeld. Stel dat we als transportmiddel gebruik maken van een vorkheftruck. Deze heeft een hefsnelheid van 0,4 m/s en een daalsnelheid van 0,4 m/s. De gemiddelde hoogte van een palletstelling bedraagt 7 m. Dit kan betekenen dat de gemiddelde hoogte waarop moet gepickt worden 3,5 m bedraagt. Hieruit kunnen we dus berekenen dat het gemiddeld
Hoofdstuk 6: Simulaties
105
hoogte hefsnelheid
+
hoogte daalsnelheid
=
3,5 m 0,4 m/s
=
17,5 s
+
3,5 m 0,4 m/s
duurt om met een vorkheftruck te picken uit een palletstelling. Daar kunnen we nog een bepaalde tijdsduur bij optellen voor het positioneren van de vorkheftruck eens men op de pickplaats is aangekomen.
De gemiddelde hoogte waarop gepickt wordt kunnen we exacter bepalen eens we de distributie kennen volgens dewelke de producten gepickt worden en waar ze in het rek liggen.
2.4
Constanten
Opnieuw merken we op dat in de verschillende klassen een aantal constanten moeten ingesteld worden. Voor de specificatie van de constanten verwijzen we naar hoofdstuk 4.
2.5
Netwerk
Bij het ingeven van het netwerk of de rechtstreekse verbindingen tussen de knopen moeten we er op letten dat het netwerkrooster begint op de eerste rij van de sheet ‘netwerk’. Dit omdat bij het lezen van de waarden in het netwerkrooster een vergelijking van rij- en kolomnummer wordt uitgevoerd om uit te maken of de cel zich boven of onder de diagonaal bevindt (dus de indices al dan niet moeten gewisseld worden).
3. Uitvoeren van een simulatie
We bespreken nu de verschillende stappen waarin een simulatie verloopt.
Hoofdstuk 6: Simulaties
106
1) We starten het programma door dubbelklik op Magazijnsimulator.exe.
2) Nadat het programma gestart is, verschijnt eerst het startscherm met de algemene informatie. Om uiteindelijk de invoer te starten, klikken we op OK of drukken we op de enter-toets.
Figuur 6.1
Startscherm Magazijnsimulator
3) Vervolgens verschijnt een inputbox waarin we de naam van het invoerbestand moeten schrijven. Daarmee bedoelen we de naam van het Excel-bestand met inbegrip van de directory waarin dit bestand zich eventueel bevindt. Bijvoorbeeld in ons geval bevinden alle invoerbestanden zich in de map ‘Magazijnlayouts’. Daarom geven we in de inputbox bijvoorbeeld de volgende naam in: Magazijnlayouts\Magazijn2.xls Nadat we de correcte naam van het invoerbestand hebben ingegeven klikken we op OK of op de enter-toets.
Hoofdstuk 6: Simulaties
107
Figuur 6.2
Inputbox Naam Invoerbestand
4) Nadat alle gegevens uit het invoerbestand zijn ingelezen verschijnt er op het scherm een messagebox waar we moeten opgeven of we al dan niet willen dat er tijdens de simulatie informatie omtrent het orderverzamelproces op het scherm wordt weergegeven in messageboxes. Het betreft hier de gegevens die op het einde van de simulatie in het uitvoerbestand geschreven worden. We bevestigen onze keuze door te klikken op de gepaste knop. Indien we ‘ja’ klikken zal er in de volgende gevallen een msgbox verschijnen: Tabel 6.2
Messageboxes tijdens simulatie
Moment van verschijnen
Inhoud msgbox
Aanvang verzamelronde
Verzamelronde (nummer)
Aanvang order
Order
(nummer)
van
verzamelronde
van
(totaal
(nummer) Aanvang orderregel
Regel
(nummer)
aantal
orderregels van het betreffende order) Aanvang orderregel
Het nummer van het rek waaruit moet gepickt worden.
Na afwerken orderregel
Resterend aantal eenheden in het rek waaruit gepickt werd
Na afwerken orderregel
Picktijd, transporttijd en afstand van de orderregel en cumulatieve tijden en afstanden
Hoofdstuk 6: Simulaties
108
Figuur 6.3
Messageboxen in- of uitschakelen
5) Onmiddellijk daarna verschijnt een volgende inputbox waarin we de duur van de delay moeten ingeven. Deze tijdsduur stemt overeen met de tijd dat een route op het scherm wordt getoond. Hierbij merken we op dat deze waarde moet opgegeven worden in ms. We nemen hier de default-waarde aan, die is ingesteld op 2 s. We bevestigen de ingegeven waarde door op OK te klikken of op de entertoets te drukken.
Figuur 6.4
Input Tijdsduur Delay
6) Daarna verschijnt de grid waarop we redeneren. Deze wordt eerst getoond met aanduiding van rekken en de ingevoerde knopen. De rekken worden gekenmerkt door rood gekleurde cellen en de knopen door gele vierkantjes die de helft zo groot zijn als de cellen. Ook het CAP (zwarte cel) is reeds te zien op voorwaarde dat de plaats van het CAP niet samenvalt met één van de knopen.
Hoofdstuk 6: Simulaties
109
Figuur 6.5
Magazijnlayout met aanduiding van knopen
7) Dan verschijnt er een messagebox. Door op OK te klikken of op de enter-toets te drukken starten we de eigenlijke simulatie van het orderverzamelproces. Nadat we op OK of op de enter-toets gedrukt hebben wordt de layout nogmaals weergegeven maar nu zonder de knopen en het CAP is nu steeds zichtbaar.
Figuur 6.6
Hoofdstuk 6: Simulaties
Starten simulatie orderverzamelproces
110
Figuur 6.7
Magazijnlayout zonder knopen
8) Bij de aanvang van een verzamelronde of pickronde worden alle rekken waaruit tijdens deze ronde producten moeten genomen worden groen afgebeeld en de bijhorende pickplaatsen lichtblauw.
9) Bij de aanvang van een orderregel wordt de startlocatie weergegeven door een geel vierkantje dat de helft zo groot als de cel en de doellocatie wordt lila afgebeeld. Rekken waaruit reeds gepickt werd worden donkerblauw weergegeven.
Hoofdstuk 6: Simulaties
111
Figuur 6.8
Momentopname simulatie
10) De uiteindelijke kortste route wordt ook weergegeven door gele vierkantjes die de helft zo groot zijn als de cellen van de grid.
Figuur 6.9
Hoofdstuk 6: Simulaties
Aanduiding kortste route
112
11) Nadat het orderverzamelproces volledig is afgewerkt, verschijnt er opnieuw een inputbox waarin de ditmaal de naam van het uitvoerbestand moeten opgeven worden. Dit gebeurt op analoge manier als bij het ingeven van de naam van het invoerbestand.
Figuur 6.10
Inputbox Naam Uitvoerbestand
4. Bespreking simulaties We hebben 5 invoerbestanden aangemaakt voor 5 verschillende magazijnlayouts. Deze zijn bijgevoegd in Appendix A en op CD-ROM. We hebben 6 simulaties uitgevoerd, waarvan twee op magazijnlayout 5. Daarbij hebben we geprobeerd alle mogelijke situaties qua layout en ook wat betreft volgorde van picken te simuleren. Daarmee bedoelen we bijvoorbeeld layouts met horizontaal georiënteerd rekken, verticaal georiënteerde rekken, met doodlopende gangen, met dwarsgangen en verzamelrondes waarbij twee maal na mekaar uit hetzelfde rek moeten picken, twee maal per wave uit het zelfde rek picken e.d. Hierbij merken we op dat één simulatie niet gelukt is. Het betreft een simulatie op magazijnlayout
5
(Magazijn5a.xls)
waarbij
de
afmetingen
bestonden
uit
onregelmatige reële getallen. Het falen van deze simulatie is te wijten aan afrondingsfouten bij het intern omzetten van reële getallen naar binaire getallen. Tabel 6.3
Voorwaarden waaronder simulaties werden uitgevoerd
Simulatie
Transportmiddel
Opslagsysteem
Magazijn 1 & 3
Vorkheftruck
Palletstellingen
Magazijn 2 & 4
Karretje
Legbordstellingen
Magazijn 5
Pallettruck
Palletstellingen
Hoofdstuk 6: Simulaties
113
Appendix A :
Simulaties
Appendix A omvat de magazijnlayouts waarmee simulaties werden uitgevoerd, alsook het invoer- en uitvoerbestand van één dergelijke simulatie uitgevoerd op magazijnlayout 2. De overige in- en uitvoerbestanden zijn terug te vinden op bijgevoegde CD-ROM.
Appendix A : Simulaties
114
ies
1
5
9
13
17
21
2
6
10
14
18
22
3
7
11
15
19
23
4
8
12
16
20
24
ies
1
5
9
13
17
21
2
6
10
14
18
22
3
7
11
15
19
23
4
8
12
16
20
24
ies
1
25
49
73
97
121
2
26
50
74
98
122
3
27
51
75
99
123
4
28
52
76
100
124
5
29
53
77
101
125
6
30
54
78
102
126
7
31
55
79
103
127
8
32
56
80
104
128
9
33
57
81
105
129
10
34
58
82
106
130
11
35
59
83
107
131
12
36
60
84
108
132
13
37
61
85
109
133
14
38
62
86
110
134
15
39
63
87
111
135
16
40
64
88
112
136
17
41
65
89
113
137
18
42
66
90
114
138
19
43
67
91
115
139
20
44
68
92
116
140
21
45
69
93
117
141
22
46
70
94
118
142
23
47
71
95
119
143
24
48
72
96
120
144
ies
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
Arial,Standaard\
1
18
33
48
63
78
93
108
2
19
34
49
64
79
94
109
3
20
35
50
65
80
95
110
4
21
36
51
66
81
96
111
5
22
37
52
67
82
97
112
6
113
7
23
38
53
68
83
98
114
8
24
39
54
69
84
99
115
9
25
40
55
70
85
100
116
10
26
41
56
71
86
101
117
11
27
42
57
72
87
102
118
12
ies
119
13
28
43
58
73
88
103
120
14
29
44
59
74
89
104
121
15
30
45
60
75
90
105
122
16
31
46
61
76
91
106
123
17
32
47
62
77
92
107
124
Nummer
ies
X
Y
Status
Previous
Distance
Routes
route
ies
wave
order
regel
stap
1
1
1
1
2
1
1
2
3 4
1 1
1 1
3 4
5
1
1
5
6
1
1
6
7
1
2
1
8
1
2
2
x 0 1 2 3 0 1 2 3 0 0 1 2 3 4 5 6 0 1 2 3 4 5 6 7 8 9 10 11 0 1 2 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10
y 8,25 6,75 6,75 6,75 6,75 6,75 6,75 6,75 6,75 6,75 6,75 5,25 3,75 2,25 2,25 2,25 2,25 2,25 2,25 3,75 5,25 6,75 8,25 9,75 11,25 11,25 11,25 11,25 11,25 11,25 11,25 11,25 11,25 9,75 8,25 6,75 5,25 3,75 2,25 2,25 2,25 2,25 2,25 2,25 3,75 5,25 6,75 8,25 9,75 11,25 11,25 11,25
rij 9,75 9,75 8,25 6,75 6,75 5,25 3,75 2,25 2,25 2,25 0,75 0,75 0,75 0,75 2,25 3,75 3,75 2,25 0,75 0,75 0,75 0,75 0,75 0,75 0,75 2,25 3,75 5,25 5,25 3,75 2,25 2,25 0,75 0,75 0,75 0,75 0,75 0,75 0,75 2,25 3,75 3,75 2,25 0,75 0,75 0,75 0,75 0,75 0,75 0,75 2,25 3,75
kolom 7 7 6 5 5 4 3 2 2 2 1 1 1 1 2 3 3 2 1 1 1 1 1 1 1 2 3 4 4 3 2 2 1 1 1 1 1 1 1 2 3 3 2 1 1 1 1 1 1 1 2 3
6 5 5 5 5 5 5 5 5 5 5 4 3 2 2 2 2 2 2 3 4 5 6 7 8 8 8 8 8 8 8 8 8 7 6 5 4 3 2 2 2 2 2 2 3 4 5 6 7 8 8 8
9
ies
1
2
3
10
1 afgifte
11
2
1
1
12
2
1
2
13
2 afgifte
0 1 2 0 1 2 3 4 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 0 1 2 3 4 5 6
11,25 11,25 11,25 11,25 11,25 11,25 9,75 8,25 8,25 6,75 6,75 5,25 3,75 2,25 2,25 2,25 2,25 2,25 2,25 2,25 3,75 5,25 6,75 6,75 6,75 6,75 6,75 6,75 6,75 6,75 8,25
3,75 5,25 6,75 6,75 8,25 9,75 9,75 9,75 9,75 9,75 8,25 8,25 8,25 8,25 6,75 5,25 3,75 2,25 2,25 0,75 0,75 0,75 0,75 2,25 2,25 3,75 5,25 6,75 8,25 9,75 9,75
3 4 5 5 6 7 7 7 7 7 6 6 6 6 5 4 3 2 2 1 1 1 1 2 2 3 4 5 6 7 7
8 8 8 8 8 8 7 6 6 5 5 4 3 2 2 2 2 2 2 2 3 4 5 5 5 5 5 5 5 5 6
Appendix B :
Code
Appendix B omvat alle klassen en formulieren die gebruikt zijn om de magazijnsimulator op te bouwen.
1. Klasse Invoer 2. Klasse Magazijn 3. Klasse Order 4. Klasse Route 5. Klasse Knoop 6. Klasse Dijkstra 7. Klasse Uitvoer 8. Formulier Intro 9. Formulier Magazijnsimulator
Appendix B: Code
132
1. Klasse Invoer VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "Invoer" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit 'In deze klasse worden alle gegevens ingelezen uit een Excel-file 'en daarna doorgegeven aan andere klassen. Const maxrekken = 200 'Het maximaal aantal rekken in het magazijn. Const maxknopen = 100 'Het maximaal aantal checkpunten of knopen in de magazijnlayout. Const maxwaves = 10 'Het maximaal aantal waves of pickrondes. Const maxorders = 20 'Het maximaal aantal orders per wave. Const maxorderregels = 30 'Het maximaal aantal orderregels per order.
Dim reklijst(1 To maxrekken, 1 To 7) As Double 'Tweedimensionale lijst waarin per rek wordt bijgehouden: '1 : reknummer '2 : x-coördinaat van het rek '3 : y-coördinaat van het rek '4 : x-coördinaat van de bijhorende pickplaats '5 : y-coördinaat van de bijhorende pickplaats '6 : nummer van het product in het rek '7 : aantal eenheden van dat product in het rek Dim CAP(1 To 2) As Double 'Eendimensionale lijst waarin van het Centraal Afgiftepunt wordt 'bijgehouden: '1 : x-coördinaat '2 : y-coördinaat Dim checkpointlijst(1 To maxknopen, 0 To 2) As Double 'Tweedimensionale lijst waarin per checkpoint wordt bijgehouden: '0 : nummer checkpoint of knoop '1 : x-coördinaat '2 : y-coördinaat Dim orders(1 To maxwaves) As Integer 'Eendimensionale lijst waarin per wave het aantal orders wordt 'bijgehouden. Dim orderregels(1 To maxwaves, 1 To maxorders) As Integer 'Tweedimensionale lijst waarin per order van een wave het aantal 'orderregels wordt bijgehouden.
Appendix B: Code
133
Dim orderlijst(1 To maxwaves, 1 To maxorders, 1 To maxorderregels, 1 To 3) As Integer 'Vierdimensionale lijst 'dimensie 1: nummer van de wave 'dimensie 2: nummer van het order 'dimensie 3: nummer van de orderregel 'dimensie 4: niveau 1: nummer van het product dat moet gepickt ' worden ' niveau 2: aantal eenheden van dat product dat moet ' gepickt worden ' niveau 3: nummer van het rek waar men het product ' kan vinden
Private Private Private Private Private Private Private Private Private Private Private Private Private
bestand As String bestandsnaam As String diepte As Double breedte As Double zijdecel As Double aantalrekken As Integer aantalcheckpoints As Integer aantalwaves As Integer aantalorderregels As Integer snelheid As Double picktijd As Double dialoogbox As VbMsgBoxResult delay As Double
Private xlApp As Excel.Application Private xlWb As Excel.Workbook Private xlWs As Excel.Worksheet ________________________________________________________________________________ 'De naam van het invoerbestand. Public Function getBestandsnaam() As String getBestandsnaam = bestandsnaam End Function Public Sub setBestandsnaam(naam As String) bestandsnaam = naam End Sub ________________________________________________________________________________ 'De diepte van het magazijn. Public Function getDiepte() As Double getDiepte = diepte End Function Public Sub setDiepte(w As Double) diepte = w End Sub ________________________________________________________________________________ 'De breedte van het magazijn. Public Function getBreedte() As Double getBreedte = breedte End Function Public Sub setBreedte(w As Double) breedte = w End Sub ________________________________________________________________________________ 'De lengte van de zijde van de cellen waarin de plattegrond is
Appendix B: Code
134
'opgedeeld. Public Function getZijdecel() As Double getZijdecel = zijdecel End Function Public Sub setZijdecel(w As Double) zijdecel = w End Sub ________________________________________________________________________________ 'Het aantal rekken in het magazijn. Public Function getAantalRekken() As Integer getAantalRekken = aantalrekken End Function Public Sub setAantalRekken(Rekken As Integer) aantalrekken = Rekken End Sub ________________________________________________________________________________ 'De lijst met de gegevens van de rekken. Public Function getReklijst(i As Integer, j As Integer) As Double getReklijst = reklijst(i, j) End Function Public Sub setReklijst(i As Integer, j As Integer, ByVal w As Double) reklijst(i, j) = w End Sub ________________________________________________________________________________ 'De lijst met de gegevens van het centraal afgiftepunt. Public Function getCAP(i As Integer) As Double getCAP = CAP(i) End Function Public Sub setCAP(i As Integer, w As Double) CAP(i) = w End Sub ________________________________________________________________________________ 'Het aantal checkpunten of knopen. Public Function getCheckpoints() As Integer getCheckpoints = aantalcheckpoints End Function Public Sub setCheckpoints(w As Integer) aantalcheckpoints = w End Sub ________________________________________________________________________________ 'De lijst met checkpunten of knopen. Public Function getCheckpointlijst(i As Integer, j As Integer) As Double getCheckpointlijst = checkpointlijst(i, j) End Function Public Sub setCheckpointlijst(i As Integer, j As Integer, w As Double) checkpointlijst(i, j) = w End Sub ________________________________________________________________________________ 'Het aantal waves of pickrondes. Public Function getWaves() As Integer getWaves = aantalwaves End Function Public Sub setWaves(w As Integer) aantalwaves = w
Appendix B: Code
135
End Sub ________________________________________________________________________________ 'Het aantal orders per wave. Public Function getOrders(i As Integer) As Integer getOrders = orders(i) End Function Public Sub setOrders(i As Integer, ByVal w As Integer) orders(i) = w End Sub ________________________________________________________________________________ 'De lijst met het aantal orderregels per order van een wave. Public Function getOrderregels(i As Integer, j As Integer) As Integer getOrderregels = orderregels(i, j) End Function Public Sub setOrderregels(i As Integer, j As Integer, ByVal w As Integer) orderregels(i, j) = w End Sub ________________________________________________________________________________ 'De lijst met de orderspecificaties. Public Function getOrderlijst(i As Integer, j As Integer, k As Integer, l As Integer) As Integer getOrderlijst = orderlijst(i, j, k, l) End Function Public Sub setOrderlijst(i As Integer, j As Integer, k As Integer, l As Integer, ByVal w As Integer) orderlijst(i, j, k, l) = w End Sub ________________________________________________________________________________ 'De snelheid van het intern transportmaterieel. Public Function getSnelheid() As Double getSnelheid = snelheid End Function Public Sub setSnelheid(w As Double) snelheid = w End Sub ________________________________________________________________________________ 'De gemiddelde tijd nodig om te picken. Public Function getPicktijd() As Double getPicktijd = picktijd End Function Public Sub setPicktijd(w As Double) picktijd = w End Sub ________________________________________________________________________________ 'Parameter om aan te geven of er tijdens de simulatie al dan niet 'dialoogvensters op het scherm verschijnen. Public Function getDialoog() As VbMsgBoxResult getDialoog = dialoogbox End Function Public Sub setDialoog(dialoog As VbMsgBoxResult) dialoogbox = dialoog End Sub ________________________________________________________________________________ 'Tijdsduur gedurende dewelke de verschillende routes zichtbaar blijven 'op het scherm.
Appendix B: Code
136
Public Function getDelay() As Double getDelay = delay End Function Public Sub setDelay(w As Double) delay = w End Sub ________________________________________________________________________________ Private Sub Afmetingen_magazijn() 'De basisafmetingen van het magazijn inlezen, d.w.z. de breedte, 'de diepte en de lengte van de zijde van de cellen waarin de 'plattegrond is opgedeeld. Call Me.setBreedte(xlWs.Range("breedte").Value) 'De breedte van het magazijn inlezen. Call Me.setDiepte(xlWs.Range("diepte").Value) 'De diepte van het magazijn inlezen. Call Me.setZijdecel(xlWs.Range("lengtezijde").Value) 'De lengte van de zijde van de cel inlezen. End Sub ________________________________________________________________________________ Private Sub Rekken() 'De gegevens van de rekken inlezen. Dim m As Integer Dim n As Integer 'tellers Dim rij As Integer Dim kolom As Integer 'Parameters om het rij- en kolomnummer bij te houden. Call Me.setAantalRekken(xlWs.Range("aantalrekken").Value) 'Het aantal rekken inlezen. rij = xlWs.Range("rekken").row kolom = xlWs.Range("rekken").column For m = 1 To Me.getAantalRekken() For n = 1 To 7 Call Me.setReklijst(m, n, xlWs.Cells(rij + m, kolom + n - 1).Value) Next n Next m 'Naargelang de waarde van n worden de volgende gegevens ingelezen: '1 : het nummer van het rek '2 : x-coördinaat van het rek '3 : y-coördinaat van het rek '4 : x-coördinaat van de bijhorende pickplaats '5 : y-coördinaat van de bijhorende pickplaats '6 : nummer van het product opgeslagen in het rek '7 : aantal eenheden van dat product opgeslagen in het rek End Sub ________________________________________________________________________________ Private Sub Plaats_CAP() 'De x- en y-coördinaat van het centraal afgiftepunt inlezen. Call Me.setCAP(1, xlWs.Range("xCAP").Value) Call Me.setCAP(2, xlWs.Range("yCAP").Value) End Sub
Appendix B: Code
137
Private Sub Checkpoints() 'Het aantal checkpoints en de x- en y-coördinaten van de 'checkpoints inlezen. Dim i As Integer Dim j As Integer 'tellers Dim rij As Integer Dim kolom As Integer 'Parameters om het rij- en kolomnummer bij te houden. Call Me.setCheckpoints(xlWs.Range("aantalcheckpoints").Value) 'Het aantal knopen inlezen. rij = xlWs.Range("checkpoint").row kolom = xlWs.Range("checkpoint").column For i = 1 To Me.getCheckpoints() For j = 0 To 2 Call Me.setCheckpointlijst(i, j, xlWs.Cells(rij + i, _ kolom + j)) Next j Next i 'Het nummer en de coördinaten inlezen. End Sub ________________________________________________________________________________ Private Sub Aantal_waves() 'Het aantal waves inlezen. Call Me.setWaves(xlWs.Range("aantalwaves").Value) End Sub ________________________________________________________________________________ Private Sub Aantal_orders_per_wave() 'Het aantal orders per wave inlezen. Dim i As Integer 'teller For i = 1 To Me.getWaves() Call Me.setOrders(i, xlWs.Range("orderswave" & i).Value) Next i End Sub ________________________________________________________________________________ Private Sub Aantal_orderregels_per_order() 'Het aantal orderregels per order van een wave inlezen. Dim m As Integer Dim n As Integer 'tellers For m = 1 To Me.getWaves() For n = 1 To Me.getOrders(m) Call Me.setOrderregels(m, n, xlWs.Range("orderregels" & n _ & "wave" & m).Value) Next n
Appendix B: Code
138
Next m End Sub ________________________________________________________________________________ Private Sub Orderlijst_vastleggen() 'De orderspecificaties inlezen. Dim l As Integer Dim m As Integer Dim n As Integer Dim t As Integer 'tellers Dim rij As Integer Dim kolom As Integer 'Parameters om het rij- en kolomnummer bij te houden. t = 1 rij = xlWs.Range("orderregels").row kolom = xlWs.Range("orderregels").column For l = 1 To Me.getWaves() For m = 1 To Me.getOrders(l) For n = 1 To Me.getOrderregels(l, m) Call Me.setOrderlijst(l, m, n, 1, xlWs.Cells(rij + t, _ kolom + 1).Value) 'Het product dat moet gepickt worden. Call Me.setOrderlijst(l, m, n, 2, xlWs.Cells(rij + t, _ kolom + 2).Value) 'Het aantal eenheden dat moet gepickt worden. Call Me.setOrderlijst(l, m, n, 3, xlWs.Cells(rij + t, _ kolom + 3).Value) 'Het rek waarin het product is opgeslagen. t = t + 1 'Rij naar beneden. Next n Next m Next l End Sub ________________________________________________________________________________ Private Sub Snelheid_en_picktijd() 'De snelheid van het intern transportmaterieel en de gemiddelde 'picktijd inlezen. Call Me.setSnelheid(xlWs.Range("snelheid").Value) Call Me.setPicktijd(xlWs.Range("picktijd").Value) End Sub ________________________________________________________________________________ Private Sub Class_Initialize() 'Klasse initialiseren. bestand = InputBox("Geef de naam van het invoerbestand op.", _ "Naam Invoerbestand", "Magazijnlayouts\Magazijn") Call Me.setBestandsnaam(App.Path & "\" & bestand) Set xlApp = New Excel.Application
Appendix B: Code
139
xlApp.Visible = False Set xlWb = xlApp.Workbooks.Open(Me.getBestandsnaam()) xlApp.Worksheets("layout").Activate Set xlWs = xlWb.ActiveSheet 'De sheet "layout" van de excel-file activeren. 'Deze sheet bevat alle informatie over de magazijnlayout. Afmetingen_magazijn Rekken Plaats_CAP xlApp.Worksheets("knopen").Activate Set xlWs = xlWb.ActiveSheet 'De sheet "knopen" van de excel-file activeren. 'Deze sheet bevat alle informatie over de controlepunten 'of knopen. Checkpoints xlApp.Worksheets("orders").Activate Set xlWs = xlWb.ActiveSheet 'De sheet "orders" van de excel-file activeren. 'Deze sheet bevat alle informatie over de orders. Aantal_waves Aantal_orders_per_wave Aantal_orderregels_per_order Orderlijst_vastleggen xlApp.Worksheets("transport").Activate Set xlWs = xlWb.ActiveSheet 'De sheet "transport" van de excel-file activeren. 'Deze sheet bevat alle informatie over het intern transport'materieel en de gemiddelde tijd nodig om te picken. Snelheid_en_picktijd xlApp.Visible = True xlApp.Quit 'Set xlApp = Nothing Call Me.setDialoog(MsgBox("Wenst U tijdens de simulatie informatie " _ & "omtrent het orderverzamelen op het scherm te zien?", _ vbYesNo, "Messageboxes in- of uitschakelen")) Call Me.setDelay(InputBox("Hoe lang moeten de routes op het scherm getoond " _ & "worden?(uitgedrukt in ms)", "Delay", "2000")) End Sub
Appendix B: Code
140
2. Klasse Magazijn VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "Magazijn" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit 'In deze klasse worden alle gegevens van het magazijn bijgehouden. Const maxrij = 100 'Het maximaal aantal rijen van de plattegrond. Const maxkolom = 100 'Het maximaal aantal kolommen van de plattegrond. Const maxrekken = 200 'Het maximaal aantal rekken in het magazijn. Const maxknopen = 100 'Het maximaal aantal checkpunten of knopen. Dim matrix(1 To maxrij, 1 To maxkolom, 1 To 4) As Double 'Driedimensionale lijst waarin per cel (rij- en kolomnummer worden 'respectievelijk aangeduid door eerste twee dimensies) wordt 'bijgehouden: '1 : x-coördinaat van de cel '2 : y-coördinaat van de cel '3 : gang of rek (0 = gang, 1 = rek) '4 : toestand van de cel Dim reklijst(1 To maxrekken, 1 To 11) As Double 'Tweedimensionale lijst waarin per rek wordt bijgehouden: '1 : reknummer '2 : x-coördinaat van het rek '3 : y-coördinaat van het rek '4 : x-coördinaat van de bijhorende pickplaats '5 : y-coördinaat van de bijhorende pickplaats '6 : nummer van product in het rek '7 : aantal eenheden van dat product in het rek '8 : rij van het rek '9 : kolom van het rek Dim CAP(1 To 4) As Double 'Eendimensionale lijst waarin van het Centraal Afgiftepunt wordt 'bijgehouden: '1 : x-coördinaat '2 : y-coördinaat '3 : rij CAP '4 : kolom CAP Dim checkpointlijst(1 To maxknopen, 0 To 4) As Double 'Tweedimensionale lijst waarin per checkpunt of knoop wordt 'bijgehouden: '0 : nummer van de knoop
Appendix B: Code
141
'1 '2 '3 '4
: : : :
x-coördinaat van de knoop y-coördinaat van de knoop rij van de knoop in kwestie kolom van de knoop in kwestie
Dim inputdata As Invoer 'Een object van de klasse Invoer. Private diepte As Double Private breedte As Double Private zijdecel As Double Private rijen As Integer Private kolommen As Integer Private aantalrekken As Integer Private aantalcheckpoints As Integer Private snelheid As Double Private picktijd As Double ________________________________________________________________________________ 'De diepte van het magazijn. Public Function getDiepte() As Double getDiepte = diepte End Function Public Sub setDiepte(w As Double) diepte = w End Sub ________________________________________________________________________________ 'De breedte van het magazijn. Public Function getBreedte() As Double getBreedte = breedte End Function Public Sub setBreedte(w As Double) breedte = w End Sub ________________________________________________________________________________ 'De lengte van de zijde van de cellen waarin de plattegrond is 'opgedeeld. Public Function getZijdecel() As Double getZijdecel = zijdecel End Function Public Sub setZijdecel(w As Double) zijdecel = w End Sub ________________________________________________________________________________ 'Het aantal rijen van de plattegrond. Public Function getRijen() As Integer getRijen = rijen End Function Public Sub setRijen(w As Integer) rijen = w End Sub ________________________________________________________________________________ 'Het aantal kolommen van de plattegrond. Public Function getKolommen() As Integer getKolommen = kolommen End Function
Appendix B: Code
142
Public Sub setKolommen(w As Integer) kolommen = w End Sub ________________________________________________________________________________ 'Het aantal rekken in het magazijn. Public Function getAantalRekken() As Integer getAantalRekken = aantalrekken End Function Public Sub setAantalRekken(Rekken As Integer) aantalrekken = Rekken End Sub ________________________________________________________________________________ 'De lijst met de gegevens van de rekken. Public Function getReklijst(i As Integer, j As Integer) As Double getReklijst = reklijst(i, j) End Function Public Sub setReklijst(i As Integer, j As Integer, ByVal w As Double) reklijst(i, j) = w End Sub ________________________________________________________________________________ 'De matrixstructuur om gegevens van de cellen in de plattegrond 'bij te houden. Public Function getMatrix(i As Integer, j As Integer, _ k As Integer) As Double getMatrix = matrix(i, j, k) End Function Public Sub setMatrix(i As Integer, _ j As Integer, k As Integer, ByVal w As Double) matrix(i, j, k) = w End Sub ________________________________________________________________________________ 'De lijst met de gegevens van het centraal afgiftepunt. Public Function getCAP(i As Integer) As Double getCAP = CAP(i) End Function Public Sub setCAP(i As Integer, w As Double) CAP(i) = w End Sub ________________________________________________________________________________ 'Het aantal checkpunten of knopen. Public Function getCheckpoints() As Integer getCheckpoints = aantalcheckpoints End Function Public Sub setCheckpoints(w As Integer) aantalcheckpoints = w End Sub ________________________________________________________________________________ 'De lijst met checkpunten of knopen. Public Function getCheckpointlijst(i As Integer, j As Integer) As Double getCheckpointlijst = checkpointlijst(i, j) End Function Public Sub setCheckpointlijst(i As Integer, j As Integer, w As Double) checkpointlijst(i, j) = w End Sub ________________________________________________________________________________
Appendix B: Code
143
'De snelheid van het intern transportmaterieel Public Function getSnelheid() As Double getSnelheid = snelheid End Function Public Sub setSnelheid(w As Double) snelheid = w End Sub ________________________________________________________________________________ 'De gemiddelde tijd nodig om te picken. Public Function getPicktijd() As Double getPicktijd = picktijd End Function Public Sub setPicktijd(w As Double) picktijd = w End Sub ________________________________________________________________________________ 'Een object van de klasse Invoer. Public Sub setInvoer(ByRef invoerobject As Invoer) Set inputdata = invoerobject End Sub Public Function getInvoer() As Invoer getInvoer = inputdata End Function ________________________________________________________________________________ Private Sub Afmetingen_magazijn() 'De basisafmetingen van het magazijn doorgeven van de klasse 'Invoer naar de klasse Magazijn. Call Me.setBreedte(inputdata.getBreedte()) Call Me.setDiepte(inputdata.getDiepte()) Call Me.setZijdecel(inputdata.getZijdecel()) End Sub ________________________________________________________________________________ Private Sub Berekening_aantal_rijen_en_kolommen() 'Aantal rijen en kolommen van de plattegrond berekenen. Call Me.setRijen(Me.getDiepte() / Me.getZijdecel()) Call Me.setKolommen(Me.getBreedte() / Me.getZijdecel()) End Sub ________________________________________________________________________________ Private Sub X_coördinaat() 'X-coördinaat van het middelpunt van elke locatie (of cel) 'van de plattegrond berekenen. Dim m As Integer Dim n As Integer 'tellers Call Me.setMatrix(1, 1, 1, Me.getZijdecel() / 2) For m = 1 To Me.getRijen() For n = 1 To Me.getKolommen() Call Me.setMatrix(m, n, 1, Me.getMatrix(1, 1, 1) + ((n - 1) _ * Me.getZijdecel())) Next n
Appendix B: Code
144
Next m 'Bij elke rij starten met de eerste kolom en dan telkens de lengte 'van de zijde van een cel bijtellen per kolom die je opschuift 'naar rechts. End Sub ________________________________________________________________________________ Private Sub Y_coördinaat() 'Y-coördinaat van het middelpunt van elke locatie (of cel) 'van de plattegrond berekenen. Dim m As Integer Dim n As Integer 'tellers Call Me.setMatrix(1, 1, 2, Me.getZijdecel() / 2) For m = 1 To Me.getRijen() For n = 1 To Me.getKolommen() Call Me.setMatrix(m, n, 2, Me.getMatrix(1, 1, 2) + ((m - 1) _ * Me.getZijdecel())) Next n Next m 'Voor elke kolom beginnen met de eerste rij en dan telkens de lengte 'van de zijde van een cel bijtellen per rij dat je opschuift naar 'beneden. End Sub ________________________________________________________________________________ Private Sub Rekken() 'De lijst met gegevens van de rekken doorgeven van de klasse 'Invoer naar de klasse Magazijn. 'Daarna nagaan welke cellen van de plattegrond overeenkomen 'met rekken. Dim m As Integer Dim n As Integer Dim i As Integer Dim j As Integer Dim k As Integer 'tellers Dim rij As Double Dim kolom As Double 'Parameters om het rij- en kolomnummer bij te houden. Call Me.setAantalRekken(inputdata.getAantalRekken()) For m = 1 To Me.getAantalRekken() For n = 1 To 7 Call Me.setReklijst(m, n, inputdata.getReklijst(m, n)) Next n Next m For i = 1 To Me.getRijen() For j = 1 To Me.getKolommen() For k = 1 To Me.getAantalRekken() If Me.getReklijst(k, 2) = Me.getMatrix(i, j, 1) Then If Me.getReklijst(k, 3) = Me.getMatrix(i, j, 2) Then rij = i
Appendix B: Code
145
kolom = j Call Me.setReklijst(k, 8, i) Call Me.setReklijst(k, 9, j) Call Me.setMatrix(i, j, 3, 1) Call Me.setMatrix(i, j, 4, 1) End If End If Next k Next j Next i For i = 1 To Me.getRijen() For j = 1 To Me.getKolommen() If Not Me.getMatrix(i, j, 3) = 1 Then Call Me.setMatrix(i, j, 3, 0) Call Me.setMatrix(i, j, 4, 0) End If Next j Next i 'Als de x- en y-coördinaten van een cel overeenkomen met 'die van een rek dan bevindt zich op die plaats een rek, 'en is matrix(i,j,3) = 1 (wat aanduidt dat het om een rek 'gaat) en matrix(i,j,4) = 1, het rij- en kolomnummer worden 'toegevoegd aan de reklijst. 'Indien de x- of y-coördinaat niet overeenkomt, dan 'wordt matrix(i,j,3)= 0 (wat aanduidt dat het om een gang gaat) 'en matrix(i,j,4) = 0. End Sub ________________________________________________________________________________ Private Sub Plaats_CAP() 'x- en y-coördinaten van het centraal afgiftepunt doorgeven 'van de klasse Invoer naar de klasse Magazijn. 'Daarna nagaan welke cel van de plattegrond daar mee 'overeenkomt. Dim i As Integer Dim j As Integer Dim m As Integer 'tellers Dim rij As Double Dim kolom As Double 'Parameters om het rij- en kolomnummer bij te houden. For m = 1 To 2 Call Me.setCAP(m, inputdata.getCAP(m)) Next m For i = 1 To Me.getRijen() For j = 1 To Me.getKolommen() If Me.getMatrix(i, j, 1) = Me.getCAP(1) Then If Me.getMatrix(i, j, 2) = Me.getCAP(2) Then rij = i kolom = j Call Me.setCAP(3, rij) Call Me.setCAP(4, kolom) Call Me.setMatrix(i, j, 3, 0) Call Me.setMatrix(i, j, 4, 2) End If End If
Appendix B: Code
146
Next j Next i 'Als de x- en y- coördinaat van een cel overeenkomen met de x- en 'y- coördinaat van het centraal afgiftepunt, dan is matrix(m,n,3) '= 0 (we beschouwen het CAP dus als een gang) en is matrix(m,n,4) '= 2 (om aan te duiden dat het om het CAP gaat) End Sub ________________________________________________________________________________ Private Sub Checkpoints() 'Het aantal checkpunten en de x- en y-coördinaten van de 'checkpunten doorgeven van de klasse Invoer naar de klasse 'Magazijn. Dim i As Integer Dim j As Integer Dim k As Integer 'tellers Dim rij As Double Dim kolom As Double 'Parameters om het rij- en kolomnummer bij te houden. Call Me.setCheckpoints(inputdata.getCheckpoints()) For i = 1 To Me.getCheckpoints() For j = 0 To 2 Call Me.setCheckpointlijst(i, j, _ inputdata.getCheckpointlijst(i, j)) Next j Next i For i = 1 To Me.getRijen() For j = 1 To Me.getKolommen() For k = 1 To Me.getCheckpoints() If Me.getCheckpointlijst(k, 1) = Me.getMatrix(i, j, 1) Then If Me.getCheckpointlijst(k, 2) = Me.getMatrix(i, j, 2) Then rij = i kolom = j Call Me.setCheckpointlijst(k, 3, rij) Call Me.setCheckpointlijst(k, 4, kolom) Call Me.setMatrix(i, j, 4, 3) End If End If Next k Next j Next i 'Als x- en y-coördinaten van een cel overeenkomen met de x- en 'y-coördinaten van een knoop dan worden het rij- en kolomnummer 'van deze cel toegevoegd aan de checkpointlijst. End Sub ________________________________________________________________________________ Private Sub Snelheid_en_picktijd() 'De snelheid van het intern transportmaterieel en de gemiddelde 'picktijd doorgeven van de klasse invoer naar de klasse magazijn. Call Me.setSnelheid(inputdata.getSnelheid()) Call Me.setPicktijd(inputdata.getPicktijd())
Appendix B: Code
147
End Sub ________________________________________________________________________________ Public Function getColor(ByVal i As Integer, ByVal j As Integer) As VBRUN.ColorConstants 'Functie om op basis van de waarde van matrix(m,n,4) een kleur aan 'de cel toe te kennen. If Me.getMatrix(i, j, 4) = 0 Then getColor = VBRUN.ColorConstants.vbWhite End If 'gang If Me.getMatrix(i, j, 4) = 1 Then getColor = VBRUN.ColorConstants.vbRed End If 'rek If Me.getMatrix(i, j, 4) = 2 Then getColor = VBRUN.ColorConstants.vbBlack End If 'CAP If Me.getMatrix(i, j, 4) = 3 Then getColor = VBRUN.ColorConstants.vbYellow End If 'route If Me.getMatrix(i, j, 4) = 4 Then getColor = VBRUN.ColorConstants.vbGreen End If 'rek waaruit gepickt moet worden tijdens pickronde If Me.getMatrix(i, j, 4) = 5 Then getColor = VBRUN.ColorConstants.vbCyan End If 'pickplaats horende bij een rek waaruit gepickt moet worden If Me.getMatrix(i, j, 4) = 6 Then getColor = VBRUN.ColorConstants.vbBlue End If 'rek waaruit gepickt werd If Me.getMatrix(i, j, 4) = 7 Then getColor = VBRUN.ColorConstants.vbMagenta End If 'volgende pickplaats van de pickronde End Function ________________________________________________________________________________ Public Sub init(invoerobject As Invoer) 'Het object van de klasse Magazijn initialiseren. Set inputdata = invoerobject Afmetingen_magazijn Berekening_aantal_rijen_en_kolommen X_coördinaat Y_coördinaat Rekken Plaats_CAP
Appendix B: Code
148
Checkpoints Snelheid_en_picktijd End Sub
Appendix B: Code
149
3. Klasse Order VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "Order" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit 'In deze klasse worden alle gegevens van de orders bijgehouden. Const maxwaves = 10 'Het maximaal aantal Const maxorders = 20 'Het maximaal aantal Const maxorderregels 'Het maximaal aantal
waves. orders per wave. = 30 orderregels per order.
Dim orders(1 To maxwaves) As Integer 'Eendimensionale lijst waarin per wave het aantal orders wordt 'bijgehouden. Dim orderregels(1 To maxwaves, 0 To maxorders) As Integer 'Tweedimensionale lijst waarbij per order van een wave het aantal 'orderregels wordt bijgehouden. Dim orderlijst(1 To maxwaves, 1 To maxorders, 1 To maxorderregels, 1 To 3) As Double 'Vierdimensionale lijst. 'dimensie 1: nummer van de wave 'dimensie 2: nummer van het order 'dimensie 3: nummer van de orderregel 'dimensie 4: niveau 1: nummer van het product dat moet gepickt worden ' niveau 2: aantal eenheden van dat product dat moet ' gepickt worden ' niveau 3: nummer van het rek waar men het product ' kan vinden Dim inputdata As Invoer 'Een object van de klasse Invoer. Private aantalwaves As Integer Private aantalorderregels As Integer ________________________________________________________________________________ 'Het aantal waves Public Function getWaves() As Integer getWaves = aantalwaves End Function Public Sub setWaves(w As Integer) aantalwaves = w End Sub ________________________________________________________________________________
Appendix B: Code
150
'Het aantal orders per wave. Public Function getOrders(i As Integer) As Integer getOrders = orders(i) End Function Public Sub setOrders(i As Integer, ByVal w As Integer) orders(i) = w End Sub ________________________________________________________________________________ 'Het aantal orderregels per order van een wave. Public Function getOrderregels(i As Integer, j As Integer) As Integer getOrderregels = orderregels(i, j) End Function Public Sub setOrderregels(i As Integer, j As Integer, ByVal w As Integer) orderregels(i, j) = w End Sub ________________________________________________________________________________ 'De lijst met de orderspecificaties. Public Function getOrderlijst(i As Integer, j As Integer, k As Integer, l _ As Integer) As Integer getOrderlijst = orderlijst(i, j, k, l) End Function Public Sub setOrderlijst(i As Integer, j As Integer, k As Integer, l _ As Integer, ByVal w As Integer) orderlijst(i, j, k, l) = w End Sub ________________________________________________________________________________ 'Een object van de klasse Invoer. Public Sub setInvoer(ByRef invoerobject As Invoer) Set inputdata = invoerobject End Sub Public Function getInvoer() As Invoer getInvoer = inputdata End Function ________________________________________________________________________________ Private Sub Aantal_waves_en_orders_en_orderregels() 'Het aantal waves, orders per wave en orderregels per order doorgeven 'van de klasse Invoer naar de klasse Order. Dim m As Integer Dim i As Integer Dim j As Integer 'tellers Call Me.setWaves(inputdata.getWaves()) 'Het aantal waves. For m = 1 To Me.getWaves() Call Me.setOrders(m, inputdata.getOrders(m)) Next m 'Het aantal orders per wave. For i = 1 To Me.getWaves() For j = 1 To Me.getOrders(i) Call Me.setOrderregels(i, j, inputdata.getOrderregels(i, j)) Next j Next i
Appendix B: Code
151
'Het aantal orderregels per order. End Sub ________________________________________________________________________________ Private Sub Orderlijst_vastleggen() 'De lijst met orderspecificaties doorgeven van de klasse Invoer 'naar de klasse Order. Dim i As Dim j As Dim k As Dim l As 'tellers
Integer Integer Integer Integer
For i = 1 To Me.getWaves() For j = 1 To Me.getOrders(i) For k = 1 To Me.getOrderregels(i, j) For l = 1 To 3 Call Me.setOrderlijst(i, j, k, l, _ inputdata.getOrderlijst(i, j, k, l)) Next l Next k Next j Next i End Sub ________________________________________________________________________________ Public Sub init(invoerobject As Invoer) 'Het object van de klasse Order initialiseren. Set inputdata = invoerobject Aantal_waves_en_orders_en_orderregels Orderlijst_vastleggen End Sub
Appendix B: Code
152
4. Klasse Route VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "Route" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit 'In deze klasse worden alle gegevens bijgehouden voor wat betreft 'de routes in het magazijn. Const maxwaves = 10 'Het maximaal aantal waves. Const maxorders = 20 'Het maximaal aantal orders per wave. Const maxorderregels = 30 'Het maximaal aantal orderregels per order. Const maxroutes = 1000 'Het maximaal aantal routes per simulatie. Const maxstappen = 50 'Het maximaal aantal stappen.
Dim pickplaats(1 To maxwaves, 0 To maxorders, 1 To maxorderregels, 1 To 4) As Double 'Vierdimensionale lijst van pickplaatsen: 'dimensie 1: nummer van de wave 'dimensie 2: nummer van het order 'dimensie 3: nummer van de orderregel 'dimensie 4: niveau 1: x-coördinaat van de pickplaats horende ' bij de orderregel ' niveau 2: y-coördinaat van de pickplaats horende ' bij de orderregel ' niveau 3: rij van de cel die met pickplaats overeenkomt ' niveau 4: kolom van de cel die met pickplaats ' overeenkomt Dim startlocatie(1 To 4) As Double 'De plaats waar men zich bevindt bij de aanvang van de orderregel, 'd.w.z. het CAP of de pickplaats van de vorige orderregel: '1 : de rij waarop de startlocatie zich bevindt '2 : de kolom waarop de startlocatie zich bevindt '3 : x-coördinaat van de startlocatie '4 : y-coördinaat van de startlocatie Dim doellocatie(1 To 4) As Double 'De plaats waar men naar toe wil, d.w.z. de pickplaats horende bij 'de orderregel of het CAP: '1 : de rij waarop de doellocatie zich bevindt '2 : de kolom waarop de doellocatie zich bevindt '3 : x-coördinaat van de doellocatie '4 : y-coördinaat van de doellocatie
Appendix B: Code
153
Dim Route(1 To maxroutes, 0 To maxstappen, 1 To 8) As Double 'Driedimensionale lijst: 'dimensie 1: nummer van de route 'dimensie 2: nummer van de stap 'dimensie 3: niveau 1: het nummer van de wave ' niveau 2: het nummer van het order ' niveau 3: het nummer van de orderregel ' niveau 4: het nummer de stap ' niveau 5: de x-coördinaat ' niveau 6: de y-coördinaat ' niveau 7: de rij van de cel ' niveau 8: de kolom van de cel Dim aantalstappen(1 To maxroutes) As Integer 'Eendimensionale lijst waarin het aantal stappen per route wordt 'bijgehouden. Private stap As Integer Private routenummer As Integer Dim dc As Magazijn 'Een object van de klasse magazijn. Dim picklijst As order 'Een object van de klasse order. ________________________________________________________________________________ 'De lijst met pickplaatsen. Public Function getPickplaats(i As Integer, j As Integer, _ k As Integer, l As Integer) As Double getPickplaats = pickplaats(i, j, k, l) End Function Public Sub setPickplaats(i As Integer, j As Integer, _ k As Integer, l As Integer, ByVal w As Double) pickplaats(i, j, k, l) = w End Sub ________________________________________________________________________________ 'De doellocatie (m.a.w. de volgende pickplaats of het CAP). Public Function getDoelLocatie(i As Integer) As Double getDoelLocatie = doellocatie(i) End Function Public Sub setDoelLocatie(i As Integer, ByVal w As Double) doellocatie(i) = w End Sub ________________________________________________________________________________ 'De plaats waar men zich bevindt bij aanvang van de orderregel. Public Function getStartLocatie(i As Integer) As Double getStartLocatie = startlocatie(i) End Function Public Sub setStartLocatie(i As Integer, ByVal w As Double) startlocatie(i) = w End Sub ________________________________________________________________________________ 'Het rangnummer van de stap in het samenstellen van de route. Public Function getStap() As Integer getStap = stap End Function
Appendix B: Code
154
Public Sub setStap(w As Integer) stap = w End Sub ________________________________________________________________________________ 'Het nummer van de route. Public Function getRoutenummer() As Integer getRoutenummer = routenummer End Function Public Sub setRoutenummer(w As Integer) routenummer = w End Sub ________________________________________________________________________________ 'Het aantal stappen van een route. Public Function getAantalStappen(i As Integer) As Integer getAantalStappen = aantalstappen(i) End Function Public Sub setAantalStappen(i As Integer, w As Integer) aantalstappen(i) = w End Sub ________________________________________________________________________________ 'De lijst met gegevens van de route. Public Function getRoute(i As Integer, j As Integer, k As Integer) As Double getRoute = Route(i, j, k) End Function Public Sub setRoute(i As Integer, j As Integer, k As Integer, ByVal w As Double) Route(i, j, k) = w End Sub ________________________________________________________________________________ 'Een object van de klasse Magazijn. Public Sub setMagazijn(ByRef magazijnobject As Magazijn) Set dc = magazijnobject End Sub Public Function getMagazijn() As Magazijn getMagazijn = dc End Function ________________________________________________________________________________ 'Een object van de klasse Order. Public Sub setOrder(ByRef orderobject As order) Set picklijst = orderobject End Sub Public Function getOrder() As order getOrder = picklijst End Function ________________________________________________________________________________ Private Sub Pickplaatscellen(l As Integer, m As Integer, n As Integer, k As Integer) 'Bepalen welke cellen pickplaatsen zijn. 'Als argumenten geven we het wavenummer, ordernummer en 'orderregelnummer mee, dit om de rij en kolom op de juiste 'plaats in de lijst met pickplaatsen op te slaan. Dim i As Integer Dim j As Integer 'tellers For i = 1 To dc.getRijen()
Appendix B: Code
155
For j = 1 To dc.getKolommen() If dc.getMatrix(i, j, 1) = dc.getReklijst(k, 4) Then If dc.getMatrix(i, j, 2) = dc.getReklijst(k, 5) Then Call Me.setPickplaats(l, m, n, 3, i) 'rij van de pickplaats Call Me.setPickplaats(l, m, n, 4, j) 'kolom van de pickplaats End If End If Next j Next i 'Als de x- en y-coördinaten van een cel overeenkomen met die van de 'de pickplaats horende bij het rek waaruit moet gepickt worden, dan 'zijn het rij- en kolomnummer van die cel gelijk aan het rij- en 'kolomnummer van de pickplaats.
End Sub ________________________________________________________________________________ Private Sub Fictief_startorder(l As Integer) 'Een fictief order invoegen in de wave om er voor te zorgen dat 'men bij het begin van de effectieve wave zich op het CAP 'bevindt, zodanig dat de verzamelronde daar start. 'Als argument geven we het nummer van de wave mee. Dim m As Integer 'teller m = 0 'Het nummer van het "fictief order" gelijk aan 0 stellen. Call picklijst.setOrderregels(l, m, 1) 'Het aantal orderregels van dat "fictief order" gelijk aan 1 'stellen. Call Me.setPickplaats(l, m, 1, 'x-coördinaat pickplaats (CAP) Call Me.setPickplaats(l, m, 1, 'y-coördinaat pickplaats (CAP) Call Me.setPickplaats(l, m, 1, 'rij van de pickplaats (CAP) Call Me.setPickplaats(l, m, 1, 'kolom van de pickplaats (CAP)
1, dc.getCAP(1)) 2, dc.getCAP(2)) 3, dc.getCAP(3)) 4, dc.getCAP(4))
End Sub ________________________________________________________________________________ Private Sub Fictief_eindorder(l As Integer) 'Een fictief order invoegen in de wave om er voor te zorgen dat 'men op het einde van de effectieve wave zich naar het CAP begeeft. 'Als argument geven we het nummer van de wave mee. Dim m As Integer 'teller m = picklijst.getOrders(l) + 1 'Het nummer van het "fictief order" gelijk stellen aan het aantal 'effectieve orders in de wave + 1. Call picklijst.setOrderregels(l, m, 1) 'Het aantal orderregels van dat "fictief order" gelijk aan 1
Appendix B: Code
156
'stellen. Call Me.setPickplaats(l, 'x-coördinaat Call Me.setPickplaats(l, 'y-coördinaat Call Me.setPickplaats(l, 'rij Call Me.setPickplaats(l, 'kolom
m, 1, 1, dc.getCAP(1)) m, 1, 2, dc.getCAP(2)) m, 1, 3, dc.getCAP(3)) m, 1, 4, dc.getCAP(4))
End Sub ________________________________________________________________________________ Private Sub Pickplaatsen_en_geen_rekken_opgegeven() 'De lijst met pickplaatsen vastleggen als in orderspecificaties de 'rekken waar de te picken producten zich bevinden niet worden op'gegeven. Dim k As Dim l As Dim m As Dim n As 'tellers
Integer Integer Integer Integer
For l = 1 To picklijst.getWaves() Fictief_startorder (l) For m = 1 To picklijst.getOrders(l) For n = 1 To picklijst.getOrderregels(l, m) For k = 1 To dc.getAantalRekken() If dc.getReklijst(k, 6) = picklijst.getOrderlijst _ (l, m, n, 1) Then Call Me.setPickplaats(l, m, n, 1, _ dc.getReklijst(k, 4)) Call Me.setPickplaats(l, m, n, 2, _ dc.getReklijst(k, 5)) Call picklijst.setOrderlijst(l, m, n, 3, _ dc.getReklijst(k, 1)) Call Pickplaatscellen(l, m, n, k) End If Next k Next n Next m 'Per orderregel gaan we als volgt te werk. Als het nummer van het 'product in het rek overeenkomt met het nummer van het product op 'de orderregel, dan zijn de x- en y-coördinaat van de pickplaats 'gelijk aan de x- en y-coördinaten van de pickplaats behorende 'bij dat rek. Fictief_eindorder (l) Next l End Sub ________________________________________________________________________________ Private Sub Pickplaatsen_en_rekken_opgegeven() 'De lijst met pickplaatsen vastleggen als in orderspecificaties de
Appendix B: Code
157
'rekken waar de te picken producten zich bevinden wel worden op'gegeven. Dim k As Dim l As Dim m As Dim n As 'tellers
Integer Integer Integer Integer
For l = 1 To picklijst.getWaves() Fictief_startorder (l) For m = 1 To picklijst.getOrders(l) For n = 1 To picklijst.getOrderregels(l, m) For k = 1 To dc.getAantalRekken() If dc.getReklijst(k, 1) = picklijst.getOrderlijst _ (l, m, n, 3) Then Call Me.setPickplaats(l, m, n, 1, _ dc.getReklijst(k, 4)) Call Me.setPickplaats(l, m, n, 2, _ dc.getReklijst(k, 5)) Call Pickplaatscellen(l, m, n, k) End If Next k Next n Next m 'Per orderregel gaan we als volgt te werk. Als het nummer van 'het rek overeenkomt met het nummer van het rek op de 'orderregel, dan zijn de x- en y-coördinaat van de pickplaats 'gelijk aan de x- en y-coördinaten van de pickplaats behorende 'bij dat rek. Fictief_eindorder (l) Next l End Sub ________________________________________________________________________________ Public Sub init(magazijnobject As Magazijn, orderobject As order) Set dc = magazijnobject Set picklijst = orderobject 'Pickplaatsen_en_geen_rekken_opgegeven Pickplaatsen_en_rekken_opgegeven End Sub
Appendix B: Code
158
5. Klasse Knoop VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "Knoop" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit 'In deze klasse worden de eigenschappen van knopen gedefinieerd. 'Daarmee bedoelen we in het bijzonder de eigenschappen die 'gebruikt worden bij het uitvoeren van Dijkstra's algoritme. Private nummer As Integer Private x As Double Private y As Double Private rij As Integer Private kolom As Integer Private status As Integer Private previous As Integer Private distance As Double _______________________________________________________________________________ 'Het rangnummer van de knoop. Public Function getNummer() As Integer getNummer = nummer End Function Public Sub setNummer(w As Integer) nummer = w End Sub _______________________________________________________________________________ 'De x-coördinaat van de knoop. Public Function getX() As Double getX = x End Function Public Sub setX(w As Double) x = w End Sub _______________________________________________________________________________ 'De y-coördinaat van de knoop. Public Function getY() As Double getY = y End Function Public Sub setY(w As Double) y = w End Sub _______________________________________________________________________________ 'Het rijnummer van de knoop. Public Function getRij() As Integer getRij = rij End Function
Appendix B: Code
159
Public Sub setRij(w As Integer) rij = w End Sub _______________________________________________________________________________ 'Het kolomnummer van de knoop. Public Function getKolom() As Integer getKolom = kolom End Function Public Sub setKolom(w As Integer) kolom = w End Sub _______________________________________________________________________________ 'De status van het label van een knoop (tijdelijk of permanent 'label). Public Function getStatus() As Integer getStatus = status End Function Public Sub setStatus(w As Integer) status = w End Sub _______________________________________________________________________________ 'De voorgaande knoop in het kortste pad. Public Function getPrevious() As Integer getPrevious = previous End Function Public Sub setPrevious(w As Integer) previous = w End Sub _______________________________________________________________________________ 'De afstand van de startknoop tot deze knoop. Public Function getDistance() As Double getDistance = distance End Function Public Sub setDistance(w As Double) distance = w End Sub _______________________________________________________________________________ Private Sub Class_Initialize() 'klasse initialiseren End Sub
Appendix B: Code
160
6. Klasse Dijkstra VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "Dijkstra" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit 'In deze klasse wordt alle informatie bijgehouden met betrekking 'tot het kortste pad algoritme van Dijkstra. Const maxknopen = 100 'Het maximaal aantal checkpunten of knopen. Dim startknopen(1 To maxknopen) As Integer 'Eendimensionale lijst waarin de nummers van de startknopen 'van het kortste pad algortime worden bijgehouden. Dim eindknopen(1 To maxknopen) As Integer 'Eendimensionale lijst waarin de nummers van de eindknopen 'van het kortste pad algortime worden bijgehouden. Dim deelafstand(1 To 3) As Double 'Eendimensionale lijst waarin de drie deelafstanden van een volledige route, 'worden bijgehouden, nl. '(1): de afstand tussen startlocatie en startknoop '(2): de afstand tussen startknoop en eindknoop '(3): de afstand tussen eindknoop en doellocatie. Dim afstand(1 To maxknopen, 1 To maxknopen) As Double 'Tweedimensionale lijst waarin de route-afstand horende bij een bepaalde 'combinatie van begin- en eindknoop wordt bijgehouden. Dim kortstepad(1 To maxknopen) As Integer 'Eendimensionale lijst waarin de knopen van het kortste pad worden 'bijgehouden.
Private aantalstart As Integer Private aantaleind As Integer Private nextnode As Integer Private beginknoop As Integer Private eindknoop As Integer Private aantalknopen As Integer ________________________________________________________________________________ 'De lijst met (de nummers van de) startknopen. Public Function getStartknopen(i As Integer) As Integer getStartknopen = startknopen(i) End Function
Appendix B: Code
161
Public Sub setStartknopen(i As Integer, w As Integer) startknopen(i) = w End Sub ________________________________________________________________________________ 'De lijst met (de nummers van de) eindknopen. Public Function getEindknopen(i As Integer) As Integer getEindknopen = eindknopen(i) End Function Public Sub setEindknopen(i As Integer, w As Integer) eindknopen(i) = w End Sub ________________________________________________________________________________ 'Het aantal startknopen. Public Function getAantalStartknopen() As Integer getAantalStartknopen = aantalstart End Function Public Sub setAantalStartknopen(w As Integer) aantalstart = w End Sub ________________________________________________________________________________ 'Het aantal eindknopen. Public Function getAantalEindknopen() As Integer getAantalEindknopen = aantaleind End Function Public Sub setAantalEindknopen(w As Integer) aantaleind = w End Sub ________________________________________________________________________________ 'De volgende knoop die een permanent label krijgt. Public Function getNextnode() As Integer getNextnode = nextnode End Function Public Sub setNextnode(w As Integer) nextnode = w End Sub ________________________________________________________________________________ 'De lijst met de afstanden die corresponderen met de drie delen 'waarin de route kan opgedeeld worden. Public Function getDeelafstand(i As Integer) As Double getDeelafstand = deelafstand(i) End Function Public Sub setDeelafstand(i As Integer, w As Double) deelafstand(i) = w End Sub ________________________________________________________________________________ 'De lijst met totale afstanden per combinatie van begin- en 'eindknoop. Public Function getAfstand(i As Integer, j As Integer) As Double getAfstand = afstand(i, j) End Function Public Sub setAfstand(i As Integer, j As Integer, w As Double) afstand(i, j) = w End Sub ________________________________________________________________________________
Appendix B: Code
162
'De beginknoop van de kortste route. Public Function getBeginKnoop() As Integer getBeginKnoop = beginknoop End Function Public Sub setBeginKnoop(w As Integer) beginknoop = w End Sub ________________________________________________________________________________ 'De eindknoop van de kortste route. Public Function getEindKnoop() As Integer getEindKnoop = eindknoop End Function Public Sub setEindKnoop(w As Integer) eindknoop = w End Sub ________________________________________________________________________________ 'Het aantal knopen in de kortste route. Public Function getAantalKnopen() As Integer getAantalKnopen = aantalknopen End Function Public Sub setAantalKnopen(w As Integer) aantalknopen = w End Sub ________________________________________________________________________________ 'De lijst met de knopen die deel uitmaken van de kortste route. Public Function getKortstePad(i As Integer) As Integer getKortstePad = kortstepad(i) End Function Public Sub setKortstePad(i As Integer, w As Integer) kortstepad(i) = w End Sub ________________________________________________________________________________ Private Sub Class_Initialize() 'Klasse initialiseren End Sub
Appendix B: Code
163
7. Klasse Uitvoer VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "Uitvoer" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit 'In deze klasse worden alle gegevens bijgehouden die uiteindelijk als 'output dienen. Const maxwaves = 10 'Het maximaal aantal waves. Const maxorders = 20 'Het maximaal aantal orders per wave. Const maxorderregels = 30 'Het maximaal aantal orderregels per order. Const maxstappen = 50 'Het maximaal aantal stappen per route. Const maxroutes = 100 'Het maximaal aantal routes per simulatie. Private bestandsnaam As String Dim dc As Magazijn Dim picklijst As order Dim weg As Route Dim picktijdlijst(1 To maxwaves, 1 To maxorders, _ 1 To maxorderregels) As Double 'Driedimensionale lijst waarin de picktijd per orderregel wordt 'bijgehouden: 'dimensie 1 : nummer van de wave 'dimensie 2 : nummer van het order 'dimensie 3 : nummer van de regel Dim afstandlijst(1 To maxwaves, 1 To maxorders, _ 1 To maxorderregels) As Double 'Driedimensionale lijst waarin de afgelegde afstand per orderregel wordt 'bijgehouden: 'dimensie 1 : nummer van de wave 'dimensie 2 : nummer van het order 'dimensie 3 : nummer van de regel Dim transporttijdlijst(1 To maxwaves, 1 To maxorders, _ 1 To maxorderregels) As Double 'Driedimensionale lijst waarin de transporttijd per orderregel wordt 'bijgehouden: 'dimensie 1 : nummer van de wave 'dimensie 2 : nummer van het order 'dimensie 3 : nummer van de regel
Appendix B: Code
164
Dim cumulatieveafstand(1 To maxwaves, 1 To maxorders, _ 1 To maxorderregels) As Double 'Driedimensionale lijst waarin de cumulatieve afgelegde afstand per 'verzamelronde wordt bijgehouden. Dim cumulatievepicktijd(1 To maxwaves, 1 To maxorders, _ 1 To maxorderregels) As Double 'Driedimensionale lijst waarin de cumulatieve picktijd per 'verzamelronde wordt bijgehouden. Dim cumulatievetransporttijd(1 To maxwaves, 1 To maxorders, _ 1 To maxorderregels) As Double 'Driedimensionale lijst waarin de cumulatieve transporttijd per 'verzamelronde wordt bijgehouden. Dim totaalafstand(1 To maxwaves) As Double 'Eendimensionale lijst waarin de totaal afgelegde afstand per 'verzamelronde wordt bijgehouden. Dim totaalpicktijd(1 To maxwaves) As Double 'Eendimensionale lijst waarin de totale picktijd per verzamelronde 'wordt bijgehouden. Dim totaaltransporttijd(1 To maxwaves) As Double 'Eendimensionale lijst waarin de totale transporttijd per verzamelronde 'wordt bijgehouden. Dim aantalstappen(1 To maxroutes) As Integer 'Eendimensionale lijst waarin het aantal stappen per route wordt 'bijgehouden. Dim Route(1 To maxroutes, 0 To maxstappen, 1 To 8) As Double 'Driedimensionale lijst: 'dimensie 1: nummer van de route 'dimensie 2: nummer van de stap 'dimensie 3: niveau 1: het nummer van de wave ' niveau 2: het nummer van het order ' niveau 3: het nummer van de orderregel ' niveau 4: het nummer de stap ' niveau 5: de x-coördinaat ' niveau 6: de y-coördinaat ' niveau 7: de rij van de cel ' niveau 8: de kolom van de cel Private xlApp As Excel.Application Private xlWb As Excel.Workbook Private xlWs As Excel.Worksheet Private bestand As String Private bstandsnaam As String Private routenummer As Double ________________________________________________________________________________ 'De naam van het uitvoerbestand. Public Function getBestandsnaam() As String getBestandsnaam = bestandsnaam End Function Public Sub setBestandsnaam(naam As String) bestandsnaam = naam End Sub ________________________________________________________________________________
Appendix B: Code
165
'Het nummer van de route. Public Function getRoutenummer() As Integer getRoutenummer = routenummer End Function Public Sub setRoutenummer(w As Integer) routenummer = w End Sub ________________________________________________________________________________ 'Het aantal stappen van een route. Public Function getAantalStappen(i As Integer) As Integer getAantalStappen = aantalstappen(i) End Function Public Sub setAantalStappen(i As Integer, w As Integer) aantalstappen(i) = w End Sub ________________________________________________________________________________ 'De cumulatief afgelegde afstand bij het afwerken van een wave. Public Function getCumulatieveAfstand(i As Integer, j As Integer, k As Integer) As Double getCumulatieveAfstand = cumulatieveafstand(i, j, k) End Function Public Sub setCumulatieveAfstand(i As Integer, j As Integer, k As Integer, w As Double) cumulatieveafstand(i, j, k) = w End Sub ________________________________________________________________________________ 'De cumulatieve tijd nodig voor het orderpicken bij het afwerken 'van een wave. Public Function getCumulatievePicktijd(i As Integer, j As Integer, _ k As Integer) As Double getCumulatievePicktijd = cumulatievepicktijd(i, j, k) End Function Public Sub setCumulatievePicktijd(i As Integer, j As Integer, _ k As Integer, w As Double) cumulatievepicktijd(i, j, k) = w End Sub ________________________________________________________________________________ 'De cumulatieve tijd nodig voor het transport bij het afwerken 'van een wave. Public Function getCumulatieveTransporttijd(i As Integer, j As Integer, _ k As Integer) As Double getCumulatieveTransporttijd = cumulatievetransporttijd(i, j, k) End Function Public Sub setCumulatieveTransporttijd(i As Integer, j As Integer, k _ As Integer, w As Double) cumulatievetransporttijd(i, j, k) = w End Sub ________________________________________________________________________________ 'De totaal afgelegde afstand bij het picken van een wave. Public Function getTotaalAfstand(i As Integer) As Double getTotaalAfstand = totaalafstand(i) End Function Public Sub setTotaalAfstand(i As Integer, w As Double) totaalafstand(i) = w End Sub
Appendix B: Code
166
________________________________________________________________________________ 'De totale tijd nodig voor het picken van een wave. Public Function getTotaalPicktijd(i As Integer) As Double getTotaalPicktijd = totaalpicktijd(i) End Function Public Sub setTotaalPicktijd(i As Integer, w As Double) totaalpicktijd(i) = w End Sub ________________________________________________________________________________ 'De totale transporttijd bij het picken een wave. Public Function getTotaalTransporttijd(i As Integer) As Double getTotaalTransporttijd = totaaltransporttijd(i) End Function Public Sub setTotaalTransporttijd(i As Integer, w As Double) totaaltransporttijd(i) = w End Sub ________________________________________________________________________________ 'De lijst met picktijden per orderregel. Public Function getPicktijdlijst(i As Integer, j As Integer, k As Integer) As Double getPicktijdlijst = picktijdlijst(i, j, k) End Function Public Sub setPicktijdlijst(i As Integer, j As Integer, k As Integer, w As _ Double) picktijdlijst(i, j, k) = w End Sub ________________________________________________________________________________ 'De lijst met transporttijden per orderregel. Public Function getTransporttijdlijst(i As Integer, j As Integer, _ k As Integer) As Double getTransporttijdlijst = transporttijdlijst(i, j, k) End Function Public Sub setTransporttijdlijst(i As Integer, j As Integer, k As Integer, _ w As Double) transporttijdlijst(i, j, k) = w End Sub ________________________________________________________________________________ 'De lijst met afstanden per orderregel. Public Function getAfstandlijst(i As Integer, j As Integer, _ k As Integer) As Double getAfstandlijst = afstandlijst(i, j, k) End Function Public Sub setAfstandlijst(i As Integer, j As Integer, k As Integer, _ w As Double) afstandlijst(i, j, k) = w End Sub ________________________________________________________________________________ 'De gevolgde route. Public Function getRoute(i As Integer, j As Integer, k As Integer) As Double getRoute = Route(i, j, k) End Function Public Sub setRoute(i As Integer, j As Integer, k As Integer, ByVal w As Double) Route(i, j, k) = w End Sub ________________________________________________________________________________
Appendix B: Code
167
Private Sub Routes() 'De lijst met de routes copiëren van de klasse Route naar de klasse 'Uitvoer. Dim i As Integer Dim j As Integer Dim k As Integer 'tellers Call Me.setRoutenummer(weg.getRoutenummer) For i = 1 To Me.getRoutenummer() Call Me.setAantalStappen(i, weg.getAantalStappen(i)) For j = 0 To Me.getAantalStappen(i) For k = 1 To 8 Call Me.setRoute(i, j, k, weg.getRoute(i, j, k)) Next k Next j Next i End Sub ________________________________________________________________________________ Private Sub Picktijden_en_afstanden_wegschrijven() 'De picktijden, transporttijden en afgelegde afstanden 'wegschrijven naar Excel-file. Dim i As Integer Dim j As Integer Dim k As Integer Dim p As Integer Dim q As Integer Dim s As Integer 'tellers Dim rij As Integer Dim kolom As Integer 'Parameters om het rij- en kolomnummer bij te houden. xlApp.Worksheets("tijd").Activate Set xlWs = xlWb.ActiveSheet 'De worksheet "tijd" van de Excel-file activeren. 'In deze sheet schrijven we de pick - en transporttijden 'en de afstand per orderregel en per wave. xlWs.Cells(1, 1) = "Tijden en afstanden" p = 1 q = 1 s = 0 rij = xlWs.Range("tijden").row kolom = xlWs.Range("tijden").column xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij,
Appendix B: Code
kolom) = "wave" kolom + 1) = "order" kolom + 2) = "orderregel" kolom + 3) = "afstand orderregel (m)" kolom + 4) = "cumulatieve afstand (m)" kolom + 5) = "transporttijd orderregel (s)" kolom + 6) = "cumulatieve transporttijd (s)" kolom + 7) = "picktijd orderregel (s)"
168
xlWs.Cells(rij, kolom + 8) = "cumulatieve picktijd (s)"
For i = 1 To picklijst.getWaves() rij = xlWs.Range("tijden").row rij = rij + p kolom = xlWs.Range("tijden").column xlWs.Cells(rij, kolom) = i For j = 1 To picklijst.getOrders(i) + 1 rij = xlWs.Range("tijden").row + q kolom = xlWs.Range("tijden").column If j = picklijst.getOrders(i) + 1 Then xlWs.Cells(rij, kolom + 1) = "afgifte" Else xlWs.Cells(rij, kolom + 1) = j End If
For k = 1 To picklijst.getOrderregels(i, j) rij = xlWs.Range("tijden").row + s kolom = xlWs.Range("tijden").column xlWs.Cells(rij + k, kolom + 2) = k xlWs.Cells(rij + k, kolom + 3) = _ Me.getAfstandlijst(i, j, k) xlWs.Cells(rij + k, kolom + 4) = _ Me.getCumulatieveAfstand(i, j, k) xlWs.Cells(rij + k, kolom + 5) = _ Me.getTransporttijdlijst(i, j, k) xlWs.Cells(rij + k, kolom + 6) = _ Me.getCumulatieveTransporttijd(i, j, k) xlWs.Cells(rij + k, kolom + 7) = _ Me.getPicktijdlijst(i, j, k) xlWs.Cells(rij + k, kolom + 8) = _ Me.getCumulatievePicktijd(i, j, k) Next k q = q + picklijst.getOrderregels(i, j) p = p + picklijst.getOrderregels(i, j) s = s + picklijst.getOrderregels(i, j) Next j Next i rij = rij + 3 kolom = xlWs.Range("tijden").column p = 0 q = 1 s = 2 For i = 1 To picklijst.getWaves xlWs.Cells(rij + p, kolom) = " Totale Afstand Wave " & i & " (m) "
Appendix B: Code
169
xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij p = p + 3 q = q + 3 s = s + 3
+ + + + +
p, q, q, s, s,
kolom + 1) kolom) = " kolom + 1) kolom) = " kolom + 1)
= Me.getTotaalAfstand(i) Totale Picktijd Wave " & i & " (s) " = Me.getTotaalPicktijd(i) Totale Transporttijd Wave " & i & " (s) " = Me.getTotaalTransporttijd(i)
Next i End Sub ________________________________________________________________________________ Private Sub Rekken_wegschrijven() 'De inhoud van de rekken wegschrijven naar Excel-file. Dim i As Integer 'teller Dim rij As Integer Dim kolom As Integer 'Parameters om het rij- en kolomnummer bij te houden. xlApp.Worksheets("rekken").Activate Set xlWs = xlWb.ActiveSheet 'De sheet "rekken" van de Excel-file activeren. 'In deze sheet schrijven we de inhoud van de rekken na het 'uitvoeren van de verschillende pickrondes. xlWs.Cells(1, 1) = "Rekken" rij = xlWs.Range("rekken").row kolom = xlWs.Range("rekken").column xlWs.Cells(rij, kolom) = "reknummer" xlWs.Cells(rij, kolom + 1) = "product" xlWs.Cells(rij, kolom + 2) = "aantal eenheden" For i = 1 To dc.getAantalRekken() xlWs.Cells(rij + i, kolom) = dc.getReklijst(i, 1) xlWs.Cells(rij + i, kolom + 1) = dc.getReklijst(i, 6) xlWs.Cells(rij + i, kolom + 2) = dc.getReklijst(i, 7) Next i End Sub ________________________________________________________________________________ Private Sub Routes_wegschrijven() 'De verschillende routes wegschrijven naar Excel-file. Dim i As Integer Dim j As Integer Dim p As Integer 'tellers Dim rij As Integer Dim kolom As Integer 'Parameters om het rij- en kolomnummer bij te houden. xlApp.Worksheets("routes").Activate Set xlWs = xlWb.ActiveSheet 'De sheet "routes" van de Excel-file activeren.
Appendix B: Code
170
'In deze sheet schrijven we de gegevens van de routes afgelegd 'in het magazijn tijdens het afhandelen van de pickrondes. xlWs.Cells(1, 1) = "Route" rij = xlWs.Range("route").row kolom = xlWs.Range("route").column xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij,
kolom) = "route" kolom + 1) = "wave" kolom + 2) = "order" kolom + 3) = "regel" kolom + 4) = "stap" kolom + 5) = "x" kolom + 6) = "y" kolom + 7) = "rij" kolom + 8) = "kolom"
p = 0 For i = 1 To Me.getRoutenummer() xlWs.Cells(rij + p + 1, kolom) = i For j = 0 To Me.getAantalStappen(i) If Me.getRoute(i, j, 2) = picklijst.getOrders(Me.getRoute(i, j, 1)) + 1 Then xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij Else xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij End If
+ p + 1, kolom + 1) = Me.getRoute(i, j, 1) + p + 1, kolom + 2) = "afgifte" + p + 1, kolom + 3) = ""
xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij
+ + + + +
+ p + 1, kolom + 1) = Me.getRoute(i, j, 1) + p + 1, kolom + 2) = Me.getRoute(i, j, 2) + p + 1, kolom + 3) = Me.getRoute(i, j, 3)
p p p p p
+ + + + +
j j j j j
+ + + + +
1, 1, 1, 1, 1,
kolom kolom kolom kolom kolom
+ + + + +
4) 5) 6) 7) 8)
= = = = =
Me.getRoute(i, Me.getRoute(i, Me.getRoute(i, Me.getRoute(i, Me.getRoute(i,
j, j, j, j, j,
4) 5) 6) 7) 8)
Next j p = p + Me.getAantalStappen(i) + 1 Next i End Sub ________________________________________________________________________________ Public Sub init(magazijnobject As Magazijn, orderobject As order, _ routeobject As Route) Set dc = magazijnobject Set picklijst = orderobject Set weg = routeobject Routes bestand = InputBox("Uitvoerbestand", "Naam Uitvoerbestand", "Uitvoer.xls")
Appendix B: Code
171
Call Me.setBestandsnaam(App.Path & "\" & bestand) Set xlApp = New Excel.Application xlApp.Visible = False Set xlWb = xlApp.Workbooks.Open(Me.getBestandsnaam())
Picktijden_en_afstanden_wegschrijven Rekken_wegschrijven Routes_wegschrijven
xlApp.Visible = True xlApp.Quit End Sub
Appendix B: Code
172
8. Formulier Intro VERSION 5.00 Begin VB.Form Intro Caption = "Intro" ClientHeight = 7545 ClientLeft = 60 ClientTop = 345 ClientWidth = 9975 LinkTopic = "Form1" ScaleHeight = 7545 ScaleWidth = 9975 WindowState = 2 'Maximized Begin VB.PictureBox picLogo Height = 1935 Left = 840 ScaleHeight = 1875 ScaleWidth = 2355 TabIndex = 8 Top = 2280 Width = 2415 End Begin VB.CommandButton cmdStart Caption = "Start" Height = 615 Left = 8880 TabIndex = 4 Top = 6720 Width = 1695 End Begin VB.Label Label9 Alignment = 2 'Center Caption = "Faculteit Toegepaste Wetenschappen" Height = 255 Left = 480 TabIndex = 7 Top = 4560 Width = 3135 End Begin VB.Label Label8 Alignment = 2 'Center Caption = " © Vakgroep Technische Bedrijfsvoering (TW18)" Height = 255 Left = 0 TabIndex = 6 Top = 5280 Width = 4095 End Begin VB.Label Label7 Alignment = 2 'Center Caption = "Scriptiebegeleider: Ing. J. Soons" Height = 255 Left = 240 TabIndex = 5 Top = 6000 Width = 3375 End
Appendix B: Code
173
Begin VB.Label Caption Height Left TabIndex Top Width End Begin VB.Label Alignment Caption Height Left TabIndex Top Width End Begin VB.Label Alignment Caption Height Left TabIndex Top Width End Begin VB.Label Alignment Caption ForeColor Height Left TabIndex Top Width End
Label5 = = = = = =
"Academiejaar 2002-2003" 255 3720 3 6960 3375
Label4 = = = = = = =
2 'Center "Auteur: Jurgen De Keyser" 255 240 2 6960 3375
Label3 = = = = = = =
2 'Center "Promotor: Prof. Dr. Ir. R. Van Landeghem" 255 240 1 5640 3375
Label1 = = = = = = = =
2 'Center "GENERIEKE SIMULATOR VOOR MAGAZIJNOPTIMALISATIE" &H000000FF& 255 3600 0 1320 4815
End Attribute VB_Name = "Intro" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False Option Explicit ________________________________________________________________________________ Private Sub cmdStart_Click() Intro.Hide End Sub ________________________________________________________________________________ Private Sub Form_Load() picLogo.Picture = LoadPicture(App.Path & "\ruglogo.bmp") End Sub
Appendix B: Code
174
10. Formulier Magazijnsimulator VERSION 5.00 Begin VB.Form Magazijnsimulator Caption = "Magazijnsimulator" ClientHeight = 3345 ClientLeft = 2970 ClientTop = 2730 ClientWidth = 5820 LinkTopic = "Form1" ScaleHeight = 1900.211 ScaleLeft = 480 ScaleMode = 0 'User ScaleWidth = 3525.577 WindowState = 2 'Maximized Begin VB.PictureBox picLayout Height = 1695 Left = 0 ScaleHeight = 1635 ScaleWidth = 4725 TabIndex = 0 Top = 845 Width = 4779 End End Attribute VB_Name = "Magazijnsimulator" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False Option Explicit Const maxknopen = 50 Dim Dim Dim Dim Dim Dim
inputdata As Invoer dc As Magazijn picklijst As order weg As Route routingalgoritme As Dijkstra outputdata As Uitvoer
Dim labelcheck As Boolean 'Boolse variable om aan te geven of alle knopen al dan niet een 'permanent label hebben. Dim closestcheck As Boolean 'Boolse vaiabele om aan te geven of een knoop wel rechtstreeks 'verbonden is met een locatie. Dim aislecheck As Boolean 'Boolse variabele om aan te geven of er zich al dan niet rekken 'bevinden langs de rechte verbinding van een knoop met een locatie 'of tussen start- en doellocatie. Dim reachcheck As Boolean 'Boolse variabele die gebruikt wordt bij het reconstrueren van de 'route. Dim dijkstracheck As Boolean 'Boolse variabele om aan te geven of het al dan niet nodig is het 'kortste pad algoritme toe te passen, indien start- en doellocatie zich 'in dezelfde enkele pickgang bevinden. Dim doubleaislecheck As Boolean 'Boolse variabele die gebruikt wordt om aan te geven of de start- en 'doellocatie zich al dan niet in dezelfde dubbele pickgang bevinden.
Appendix B: Code
175
Dim dx As Double Dim dy As Double Dim x As Double Dim y As Double Dim row As Integer Dim column As Integer 'Parameters die gebruikt worden om de controles op rechtstreekse 'verbindingen en aanwezigheid van rekken uit te voeren. Dim start As Integer Dim eind As Integer Dim a As Integer Dim b As Integer 'Parameters om het rij- en kolomnummer bij te houden. Dim l As Integer Dim m As Integer Dim w As Integer Dim o As Integer Dim r As Integer 'tellers Private xlApp As Excel.Application Private xlWb As Excel.Workbook Private xlWs As Excel.Worksheet Private knopen As Collection Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) ________________________________________________________________________________ Private Sub Form_paint() 'Het magazijn(en dus bijhorende grid), de rekken, de pickplaatsen 'en de route op het scherm tekenen. Dim i As Integer Dim j As Integer 'tellers Dim color As ColorConstants 'variabele om het kleur bij te houden. picLayout.Move Magazijnsimulator.ScaleLeft, _ Magazijnsimulator.ScaleTop, Magazijnsimulator.ScaleWidth, _ Magazijnsimulator.ScaleHeight picLayout.Scale (0, 0)-(dc.getBreedte(), dc.getDiepte()) picLayout.Cls For i = 1 To dc.getRijen() For j = 1 To dc.getKolommen() color = dc.getColor(i, j) If color = vbYellow Then picLayout.Line ((j - 1) * dc.getZijdecel(), (i - 1) * _ dc.getZijdecel())-(j * dc.getZijdecel(), i * _ dc.getZijdecel()), vbWhite, BF picLayout.Line (dc.getMatrix(i, j, 1) - _ (dc.getZijdecel() / 4), dc.getMatrix(i, j, 2) - _ (dc.getZijdecel() / 4))-(dc.getMatrix(i, j, 1) + _ (dc.getZijdecel() / 4), dc.getMatrix(i, j, 2) + _ (dc.getZijdecel() / 4)), color, BF
Appendix B: Code
176
Else picLayout.Line ((j - 1) * dc.getZijdecel(), (i - 1) * _ dc.getZijdecel())-(j * dc.getZijdecel(), i * _ dc.getZijdecel()), color, BF End If 'De cellen met "bijzondere toestand" in de aangepaste 'kleur tekenen. picLayout.Line ((j - 1) * dc.getZijdecel(), (i - 1) * _ dc.getZijdecel())-(j * dc.getZijdecel(), i * _ dc.getZijdecel()), , B 'De grid er over tekenen. Next j Next i End Sub ________________________________________________________________________________ Private Sub startlocatie() 'De plaats bepalen waar we ons bevinden bij de aanvang van een 'orderregel. Dus de pickplaats van de vorige orderregel of het CAP. Dim order As Integer Dim regel As Integer 'Parameters om het order- en orderregelnummer bij te houden. If r = 1 Then order = o - 1 regel = picklijst.getOrderregels(w, order) Else order = o regel = r - 1 End If 'Als het nummer van de orderregel = 1 dan is de vorige regel 'de laatst regel van het vorige order (vandaar de invoering van 'een fictief startorder in de klasse Route). In de andere gevallen 'is de vorige regel de vorige orderregel van hetzelfde order. Call weg.setStartLocatie(1, weg.getPickplaats(w, order, regel, 3)) 'Rij van de startlocatie. Call weg.setStartLocatie(2, weg.getPickplaats(w, order, regel, 4)) 'Kolom van de startlocatie. Call weg.setStartLocatie(3, weg.getPickplaats(w, order, regel, 1)) 'X-coördinaat van de startlocatie. Call weg.setStartLocatie(4, weg.getPickplaats(w, order, regel, 2)) 'Y-coördinaat van de startlocatie. Call dc.setMatrix(weg.getStartLocatie(1), weg.getStartLocatie(2), _ 4, 3) 'Toestand van de cel meegeven. End Sub ________________________________________________________________________________ Private Sub doellocatie() 'De plaats bepalen waar we naartoe moeten om de orderregel af te 'handelen, dus de pickplaats horende bij de orderregel of het CAP. Call weg.setDoelLocatie(1, weg.getPickplaats(w, o, r, 3)) 'Rij van de doellocatie. Call weg.setDoelLocatie(2, weg.getPickplaats(w, o, r, 4))
Appendix B: Code
177
'Kolom van de doellocatie. Call weg.setDoelLocatie(3, weg.getPickplaats(w, o, r, 1)) 'X-coördinaat van de doellocatie. Call weg.setDoelLocatie(4, weg.getPickplaats(w, o, r, 2)) 'Y-coördinaat van de doelocatie. Call dc.setMatrix(weg.getDoelLocatie(1), weg.getDoelLocatie(2), _ 4, 7) 'Toestand van de cel meegeven. If inputdata.getDialoog() = vbYes Then If o = picklijst.getOrders(w) + 1 Then MsgBox " Afzetten van de verzamelde orders bij het centraal afgiftepunt." Else MsgBox " Het rek waaruit moet gepickt worden is rek nummer " & _ picklijst.getOrderlijst(w, o, r, 3) End If End If End Sub ________________________________________________________________________________ Private Sub Dijkstra_check() 'Controleren of het kortste pad algoritme nodig is voor het bepalen 'van de route, zonder rekening te houden met de mogelijkheid van een 'dubbele pickgang. dijkstracheck = True 'x-coördinaten start- en doellocatie zijn gelijk. If weg.getStartLocatie(3) = weg.getDoelLocatie(3) Then dy = weg.getDoelLocatie(4) - weg.getStartLocatie(4) x = weg.getStartLocatie(3) y = weg.getStartLocatie(4) Controle_knopen_in_Yrichting (weg.getDoelLocatie(4)) row = weg.getStartLocatie(1) column = weg.getStartLocatie(2) Controle_op_rekken_in_Yrichting (weg.getDoelLocatie(1)) If closestcheck Then If aislecheck Then dijkstracheck = False End If End If End If 'y-coördinaten start- en doellocatie zijn gelijk. If weg.getStartLocatie(4) = weg.getDoelLocatie(4) Then dx = weg.getDoelLocatie(3) - weg.getStartLocatie(3) x = weg.getStartLocatie(3) y = weg.getStartLocatie(4) Controle_knopen_in_Xrichting (weg.getDoelLocatie(3)) row = weg.getStartLocatie(1) column = weg.getStartLocatie(2) Controle_op_rekken_in_Xrichting (weg.getDoelLocatie(2)) If closestcheck Then If aislecheck Then dijkstracheck = False End If
Appendix B: Code
178
End If End If End Sub ________________________________________________________________________________ Private Sub Controle_knopen_in_Xrichting(target As Double) 'Controleren of een knoop die dezelfde y-coördinaat heeft als 'een start- of doellocatie wel rechtstreeks verbonden is met 'die locatie, of met andere woorden of er zich nog andere knopen 'bevinden tussen de locatie en de knoop. 'target = x-coördinaat knoop 'OF 'Controleren of er zich tussen start- en doellocatie nog knopen 'bevinden in het geval dat deze twee locaties in dezelfde gang 'liggen en dus dezelfde y-coördinaat hebben. 'target = x-coördinaat doellocatie Dim n As Integer 'teller closestcheck = True If dx < 0 Then Do Until x = target + dc.getZijdecel() x = x - dc.getZijdecel() For n = 1 To dc.getCheckpoints() If dc.getCheckpointlijst(n, 1) = x Then If dc.getCheckpointlijst(n, 2) = y Then closestcheck = False End If End If Next n Loop End If 'Als het verschil van de x-coördinaten tussen de twee punten 'negatief is, verplaatsen we ons telkens een cel naar links 'in het rooster, tot we de cel rechts van de doellocatie of 'knoop bereiken en controleren we of de coördinaten van het 'middelpunt van de cel overeen komen met die van een knoop. 'Indien dit het geval is, krijgt de variabele closestcheck de 'waarde false. If dx > 0 Then Do Until x = target - dc.getZijdecel() x = x + dc.getZijdecel() For n = 1 To dc.getCheckpoints() If dc.getCheckpointlijst(n, 1) = x Then If dc.getCheckpointlijst(n, 2) = y Then closestcheck = False End If End If Next n Loop End If 'Indien het verschil van de x-coördinaten positief is, passen we 'hetzelfde principe toe, waarbij we telkens een cel naar rechts 'opschuiven in het rooster tot we de cel links van de doellocatie 'of knoop bereiken. End Sub
Appendix B: Code
179
________________________________________________________________________________ Private Sub Controle_knopen_in_Yrichting(target As Double) 'Controleren of een knoop die dezelfde x-coördinaat heeft als 'een start- of doellocatie wel rechtstreeks verbonden is met 'die locatie, of met andere woorden of er zich nog andere knopen 'bevinden tussen de locatie en de knoop. 'target = y-coördinaat knoop 'OF 'Controleren of er zich tussen start- en doellocatie nog knopen 'bevinden in het geval dat deze twee locaties in dezelfde gang 'liggen en dus dezelfde x-coördinaat hebben. 'target = y-coördinaat doellocatie Dim n As Integer 'teller closestcheck = True If dy < 0 Then Do Until y = target + dc.getZijdecel() y = y - dc.getZijdecel() For n = 1 To dc.getCheckpoints() If dc.getCheckpointlijst(n, 1) = x Then If dc.getCheckpointlijst(n, 2) = y Then closestcheck = False End If End If Next n Loop End If 'Als het verschil van de y-coördinaten negatief is, verplaatsen we 'ons telkens een cel naar boven in het rooster tot we de cel onder 'de doellocatie of knoop bereiken en passen dan hetzelfde principe 'als in de bovenstaande procedure. If dy > 0 Then Do Until y = target - dc.getZijdecel() y = y + dc.getZijdecel() For n = 1 To dc.getCheckpoints() If dc.getCheckpointlijst(n, 1) = x Then If dc.getCheckpointlijst(n, 2) = y Then closestcheck = False End If End If Next n Loop End If 'Als het verschil van de y-coördinaten positief is, verplaatsen we 'ons telkens een cel naar beneden in het rooster tot we de cel 'boven de doellocatie of knoop bereiken en passen opnieuw hetzelfde 'principe toe. End Sub ________________________________________________________________________________ Private Sub Controle_op_rekken_in_Xrichting(target As Integer) 'Controleren of er zich op de rechte lijn (evenwijdig met de x-as) 'tussen de start- of doellocatie en de knoop, die dezelfde y'coördinaat hebben, geen rekken bevinden. 'target = kolomnummer knoop 'OF 'Controleren of zich op de rechte lijn tussen de start- en doel-
Appendix B: Code
180
'locatie geen rekken bevinden, in het geval ze in dezelfde gang 'liggen (dezelfde y-coördinaat hebben). 'target = kolomnummer doellocatie Dim k As Integer 'teller aislecheck = True If column < target Then Do Until column = target column = column + 1 For k = 1 To dc.getAantalRekken() If dc.getMatrix(row, column, 1) = dc.getReklijst(k, 2) Then If dc.getMatrix(row, column, 2) = dc.getReklijst(k, 3) Then aislecheck = False End If End If Next k Loop End If 'Als het kolomnummer kleiner is dan het kolomnummer van de doel'locatie of knoop dan verplaatsen we ons cel per cel naar rechts 'in het rooster tot we de knoop of doellocatie bereiken en con'troleren telkens of de cel in kwestie een rek is. Dit doen we 'door vergelijking van de coördinaten van het rek met deze van 'de cel. Indien dit het geval is dan krijgt de variabele 'aislecheck de waarde false. If column > target Then Do Until column = target column = column - 1 For k = 1 To dc.getAantalRekken() If dc.getMatrix(row, column, 1) = dc.getReklijst(k, 2) Then If dc.getMatrix(row, column, 2) = dc.getReklijst(k, 3) Then aislecheck = False End If End If Next k Loop End If 'Als het kolomnummer groter is dan het kolomnummer van de doel'locatie of knoop dan verplaatsen we ons cel per cel naar links 'in het rooster tot we de knoop of doellocatie bereiken en con'troleren telkens of de cel in kwestie een rek is. Dit doen we 'door vergelijking van de coördinaten van het rek met deze van 'de cel. End Sub ________________________________________________________________________________ Private Sub Controle_op_rekken_in_Yrichting(target As Integer) 'Controleren of er zich op de rechte lijn (evenwijdig met de y-as) 'tussen de start- of doellocatie en de knoop, die dezelfde x'coördinaat hebben, geen rekken bevinden. 'target = rijnummer knoop 'OF 'Controleren of zich op de rechte lijn tussen de start- en doel'locatie geen rekken bevinden, in het geval ze in dezelfde gang 'liggen (dezelfde x-coördinaat hebben). 'target = rijnummer doellocatie
Appendix B: Code
181
Dim k As Integer 'teller aislecheck = True If row < target Then Do Until row = target row = row + 1 For k = 1 To dc.getAantalRekken() If dc.getMatrix(row, column, 1) = dc.getReklijst(k, 2) Then If dc.getMatrix(row, column, 2) = dc.getReklijst(k, 3) Then aislecheck = False End If End If Next k Loop End If 'Als het rijnummer kleiner is dan het rijnummer van de doellocatie of 'knoop dan verplaatsen we ons cel per cel naar boven in het rooster 'tot we de knoop of doellocatie bereiken en passen dan hetzelfde prin'cipe toe als in de procedure hierboven. If row > target Then Do Until row = target row = row - 1 For k = 1 To dc.getAantalRekken() If dc.getMatrix(row, column, 1) = dc.getReklijst(k, 2) Then If dc.getMatrix(row, column, 2) = dc.getReklijst(k, 3) Then aislecheck = False End If End If Next k Loop End If 'Als het rijnummer groter is dan het rijnummer van de doellocatie of 'knoop dan verplaatsen we ons cel per cel naar beneden in het rooster 'tot we de knoop of doellocatie bereiken en passen dan hetzelfde prin'cipe toe als in de procedure hierboven. End Sub ________________________________________________________________________________ Private Sub Dubbele_gang_check() 'Controleren of de start- en doellocatie in dezelfde dubbele pickgang liggen. 'Daarvoor controleren we of het rij- of kolomnummer van de beide locaties één 'verschillen. Indien dit het geval is verplaatsen we ons één cel zodanig dat we 'op een rechte lijn met de doellocatie komen te staan. Deze cel beschouwen we 'dan als de nieuwe startlocatie. Daarna hanteren we hetzelfde principe als in 'het geval waarbij de beide locaties in dezelfde enkele pickgang liggen. doubleaislecheck = False a = weg.getStartLocatie(1) b = weg.getStartLocatie(2) Call weg.setStap(0) Route_vastleggen If b = weg.getDoelLocatie(2) + 1 Then If dc.getMatrix(a, b - 1, 3) = 0 Then dy = weg.getDoelLocatie(4) - weg.getStartLocatie(4) x = weg.getStartLocatie(3) - dc.getZijdecel() y = weg.getStartLocatie(4) Controle_knopen_in_Yrichting (weg.getDoelLocatie(4))
Appendix B: Code
182
a = a b = b - 1 column = b Controle_op_rekken_in_Yrichting (weg.getDoelLocatie(1)) If closestcheck = True Then If aislecheck = True Then doubleaislecheck = True End If End If If doubleaislecheck Then Kortste_pad_zonder_Dijkstra_dubbele_gang End If End If End If If b = weg.getDoelLocatie(2) - 1 Then If dc.getMatrix(a, b + 1, 3) = 0 Then dy = weg.getDoelLocatie(4) - weg.getStartLocatie(4) x = weg.getStartLocatie(3) + dc.getZijdecel() y = weg.getStartLocatie(4) Controle_knopen_in_Yrichting (weg.getDoelLocatie(4)) a = a b = b + 1 column = b Controle_op_rekken_in_Yrichting (weg.getDoelLocatie(1)) If closestcheck = True Then If aislecheck = True Then doubleaislecheck = True End If End If If doubleaislecheck Then Kortste_pad_zonder_Dijkstra_dubbele_gang End If End If End If If a = weg.getDoelLocatie(1) + 1 Then If dc.getMatrix(a - 1, b, 3) = 0 Then dx = weg.getDoelLocatie(3) - weg.getStartLocatie(3) x = weg.getStartLocatie(3) y = weg.getStartLocatie(4) - dc.getZijdecel Controle_knopen_in_Xrichting (weg.getDoelLocatie(3)) a = a - 1 b = b row = a Controle_op_rekken_in_Xrichting (weg.getDoelLocatie(2)) If closestcheck = True Then If aislecheck = True Then doubleaislecheck = True End If End If If doubleaislecheck Then Kortste_pad_zonder_Dijkstra_dubbele_gang End If End If
Appendix B: Code
183
End If If a = weg.getDoelLocatie(1) - 1 Then If dc.getMatrix(a + 1, b, 3) = 0 Then dx = weg.getDoelLocatie(3) - weg.getStartLocatie(3) x = weg.getStartLocatie(3) y = weg.getStartLocatie(4) + dc.getZijdecel() Controle_knopen_in_Xrichting (weg.getDoelLocatie(3)) a = a + 1 b = b row = a Controle_op_rekken_in_Xrichting (weg.getDoelLocatie(2)) If closestcheck = True Then If aislecheck = True Then doubleaislecheck = True End If End If If doubleaislecheck Then Kortste_pad_zonder_Dijkstra_dubbele_gang End If End If End If End Sub ________________________________________________________________________________ Private Sub Kortste_pad_zonder_Dijkstra_dubbele_gang() 'Bepalen van de kortste route zonder toepassing van het kortste 'pad algoritme en indien start- en doellocaie in dezelfde enkele 'pickgang liggen. Call dc.setMatrix(a, b, 4, 3) Call weg.setStap(weg.getStap + 1) Route_vastleggen If a = weg.getDoelLocatie(1) Then Call outputdata.setAfstandlijst(w, o, r, Abs(dc.getMatrix(a, b, 1) - _ weg.getDoelLocatie(3)) + dc.getZijdecel) End If If b = weg.getDoelLocatie(2) Then Call outputdata.setAfstandlijst(w, o, r, Abs(dc.getMatrix(a, b, 2) - _ weg.getDoelLocatie(4)) + dc.getZijdecel) End If
If a = weg.getDoelLocatie(1) Then If b = weg.getDoelLocatie(2) Then reachcheck = True Call dc.setMatrix(a, b, 4, 3) Else reachcheck = False End If Else reachcheck = False End If
Do While Not reachcheck Call Cel_zoeken(weg.getDoelLocatie(1), weg.getDoelLocatie(2))
Appendix B: Code
184
Loop End Sub ________________________________________________________________________________ Private Sub Kortste_pad_zonder_Dijkstra_enkele_gang() 'Bepalen van de kortste route zonder toepassing van het kortste 'pad algoritme en indien start- en doellocaie in dezelfde enkele 'pickgang liggen. Call weg.setStap(0) a = weg.getStartLocatie(1) b = weg.getStartLocatie(2) Route_vastleggen If a = weg.getDoelLocatie(1) Then Call outputdata.setAfstandlijst(w, o, r, Abs(dc.getMatrix(a, b, 1) _ - weg.getDoelLocatie(3))) End If If b = weg.getDoelLocatie(2) Then Call outputdata.setAfstandlijst(w, o, r, Abs(dc.getMatrix(a, b, 2) _ - weg.getDoelLocatie(4))) End If If weg.getStartLocatie(1) = weg.getDoelLocatie(1) Then If weg.getStartLocatie(2) = weg.getDoelLocatie(2) Then reachcheck = True Call dc.setMatrix(a, b, 4, 3) Else reachcheck = False End If Else reachcheck = False End If Do While Not reachcheck Call Cel_zoeken(weg.getDoelLocatie(1), weg.getDoelLocatie(2)) Loop End Sub ________________________________________________________________________________ Private Sub Kortste_pad_met_Dijkstra() 'Bepalen van de kortste route met behulp van het kortste pad 'algoritme. Startknopen_en_eindknopen_en_kortstepad_initialiseren Startknopen_Dijkstra Eindknopen_Dijkstra Kortste_route Kortste_route_reconstrueren End Sub ________________________________________________________________________________ Private Sub Startknopen_Dijkstra() 'De startknopen bepalen voor het kortste pad algoritme, of met 'andere woorden de knopen bepalen die rechtstreeks verbonden zijn 'met de startlocatie. 'Werkwijze: 'Conroleren of de x- of y-coördinaat van een knoop overeenkomt met 'deze van de startlocatie. Dan controleren of er geen andere knopen
Appendix B: Code
185
'of rekken liggen op de rechte verbinding tussen de startlocatie 'en de knoop. De knoop toevoegen aan de lijst van de startknopen. Dim i As Integer Dim t As Integer 'tellers t = 0 For m = 1 To dc.getCheckpoints() 'Knoop en startlocatie hebben dezelfde x-coördinaat. If dc.getCheckpointlijst(m, 1) = weg.getStartLocatie(3) Then If Not dc.getCheckpointlijst(m, 2) = _ weg.getStartLocatie(4) Then dy = dc.getCheckpointlijst(m, 2) - weg.getStartLocatie(4) x = weg.getStartLocatie(3) y = weg.getStartLocatie(4) Call Controle_knopen_in_Yrichting(dc.getCheckpointlijst(m, 2)) row = weg.getStartLocatie(1) column = weg.getStartLocatie(2) Call Controle_op_rekken_in_Yrichting(dc.getCheckpointlijst(m, 3)) If closestcheck Then If aislecheck Then t = t + 1 Call routingalgoritme.setStartknopen(t, _ knopen.Item(m).getNummer) End If End If End If End If 'Knoop en startlocatie hebben dezelfde y-coördinaat. If dc.getCheckpointlijst(m, 2) = weg.getStartLocatie(4) Then If Not dc.getCheckpointlijst(m, 1) = _ weg.getStartLocatie(3) Then dx = dc.getCheckpointlijst(m, 1) - weg.getStartLocatie(3) x = weg.getStartLocatie(3) y = weg.getStartLocatie(4) Call Controle_knopen_in_Xrichting(dc.getCheckpointlijst(m, 1)) row = weg.getStartLocatie(1) column = weg.getStartLocatie(2) Call Controle_op_rekken_in_Xrichting(dc.getCheckpointlijst(m, 4)) If closestcheck Then If aislecheck Then t = t + 1 Call routingalgoritme.setStartknopen(t, _ knopen.Item(m).getNummer) End If End If End If End If
Appendix B: Code
186
Next m Call routingalgoritme.setAantalStartknopen(t) End Sub ________________________________________________________________________________ Private Sub Eindknopen_Dijkstra() 'De eindknopen bepalen voor het kortste pad algoritme, of met 'andere woorden de knopen bepalen die die rechtstreeks verbonden zijn 'met de doellocatie. Dim i As Integer Dim t As Integer 'tellers t = 0 For m = 1 To dc.getCheckpoints() 'Knoop en doellocatie hebben dezelfde x-coördinaat. If dc.getCheckpointlijst(m, 1) = weg.getDoelLocatie(3) Then If Not dc.getCheckpointlijst(m, 2) = _ weg.getDoelLocatie(4) Then dy = dc.getCheckpointlijst(m, 2) - weg.getDoelLocatie(4) x = weg.getDoelLocatie(3) y = weg.getDoelLocatie(4) Call Controle_knopen_in_Yrichting(dc.getCheckpointlijst(m, 2)) row = weg.getDoelLocatie(1) column = weg.getDoelLocatie(2) Call Controle_op_rekken_in_Yrichting(dc.getCheckpointlijst(m, 3)) If closestcheck Then If aislecheck Then t = t + 1 Call routingalgoritme.setEindknopen(t, _ knopen.Item(m).getNummer) End If End If End If End If 'Knoop en doellocatie hebben dezelfde y-coördinaat. If dc.getCheckpointlijst(m, 2) = weg.getDoelLocatie(4) Then If Not dc.getCheckpointlijst(m, 1) = _ weg.getDoelLocatie(3) Then dx = dc.getCheckpointlijst(m, 1) - weg.getDoelLocatie(3) x = weg.getDoelLocatie(3) y = weg.getDoelLocatie(4) Call Controle_knopen_in_Xrichting(dc.getCheckpointlijst(m, 1)) row = weg.getDoelLocatie(1) column = weg.getDoelLocatie(2) Call Controle_op_rekken_in_Xrichting(dc.getCheckpointlijst(m, 4)) If closestcheck Then If aislecheck Then t = t + 1
Appendix B: Code
187
Call routingalgoritme.setEindknopen(t, _ knopen.Item(m).getNummer) End If End If End If End If Next m Call routingalgoritme.setAantalEindknopen(t) End Sub ________________________________________________________________________________ Private Sub Dijkstra(i As Integer) 'Het kortste pad algoritme waarbij het nummer van de startknoop als 'argument wordt meegegeven. xlApp.Worksheets("netwerk").Activate Set xlWs = xlWb.ActiveSheet 'De sheet waarin het rooster met rechtstreekse verbindingen staat 'activeren. First_iteration (i) Next_node Check_permanent_label Do While Not labelcheck Next_iteration (routingalgoritme.getNextnode()) Next_node Check_permanent_label Loop End Sub ________________________________________________________________________________ Private Sub First_iteration(i As Integer) 'De eerste stap in het kortste pad algoritme. 'i is het nummer van de startknoop. Dim j As Integer 'teller Dim rij As Integer Dim kolom As Integer 'Parameters om het rij- en kolomnummer bij te houden. Call knopen.Item(i).setStatus(2) Call knopen.Item(i).setPrevious(i) Call knopen.Item(i).setDistance(0) 'De eigenschappen van de startknoop vastleggen, d.w.z. een permanent 'label meegeven, de vorige knoop is de startknoop zelf en de afstand 'vanaf de startknoop is 0. rij = xlWs.Range("netwerk").row + i kolom = xlWs.Range("netwerk").column 'Rooster met de rechtstreekse verbindingen doorlopen. For j = 1 To dc.getCheckpoints() rij = rij kolom = kolom + 1
Appendix B: Code
188
'Boven de diagonaal van het rooster. If rij < kolom Then If xlWs.Cells(rij, kolom).Value <> "" Then Call knopen.Item(kolom - 1).setStatus(1) Call knopen.Item(kolom - 1).setPrevious(i) Call knopen.Item(kolom - 1).setDistance(knopen.Item _ (i).getDistance + xlWs.Cells(rij, kolom).Value) End If End If 'Als de cel in het rooster niet leeg is dan bestaat er een 'rechtstreekse verbinding tussen de startknoop (komt overeen 'met het rijnummer) en de knoop waarvan het nummer overeen'komt met het kolomnummer. Deze knoop krijgt dan een tijde'lijk label en de afstand is gelijk aan de waarde opgegeven 'in het rooster. 'Onder de diagonaal van het rooster. If rij > kolom Then xlWs.Cells(rij, kolom).Value = xlWs.Cells(kolom, rij).Value 'Indexen wisselen want het rooster is symmetrisch en de waarden 'onder de diagonaal zijn niet expliciet opgegeven. If xlWs.Cells(rij, kolom).Value <> "" Then Call knopen.Item(kolom - 1).setStatus(1) Call knopen.Item(kolom - 1).setPrevious(i) Call knopen.Item(kolom - 1).setDistance(knopen.Item _ (i).getDistance + xlWs.Cells(rij, kolom).Value) End If End If 'Zelfde principe als hierboven. Next j End Sub ________________________________________________________________________________ Private Sub Next_node() 'De volgende knoop bepalen die een permanent label krijgt. 'Dit is de knoop met de kleinste distance-waarde uit de 'verzameling van alle knopen met een tijdelijk label. Dim i As Integer Dim t As Integer 'tellers t = 0 For i = 1 To dc.getCheckpoints If knopen.Item(i).getStatus = 1 Then t = t + 1 If t = 1 Then Call routingalgoritme.setNextnode(i) Else If knopen.Item(i).getDistance < _ knopen.Item(routingalgoritme.getNextnode()).getDistance Then Call routingalgoritme.setNextnode(i) End If End If
Appendix B: Code
189
End If Next i 'De eerste knoop met een tijdelijk label die we vinden wordt 'aanvankelijk de volgende knoop. Telkens we daarna een knoop 'vinden met een tijdelijk label en waarvan de distance-waarde 'kleiner is dan deze van de huidige nextnode, wijzigen we de 'waarde van nextnode. Call knopen.Item(routingalgoritme.getNextnode()).setStatus(2) 'De knoop een permanent label geven (status = 2). End Sub ________________________________________________________________________________ Private Sub Check_permanent_label() 'Procedure om te controleren of alle knopen een permanent label hebben. 'Werkwijze: 'Alle knopen in de collection overlopen en het aantal knopen 'met een permanent label tellen. Is dit aantal gelijk aan het 'aantal checkpoints (knopen) dan hebben alle knopen een permanent 'label. Dim i As Integer Dim t As Integer 'tellers t = 0 For i = 1 To dc.getCheckpoints() If knopen.Item(i).getStatus = 2 Then t = t + 1 End If Next i If t = dc.getCheckpoints Then labelcheck = True Else labelcheck = False End If End Sub ________________________________________________________________________________ Private Sub Next_iteration(i As Integer) 'De verschillende iteraties van het kortste pad algoritme. 'Werkwijze: 'Knopen die nog geen label hebben en rechstreeks verbonden zijn 'met de huidige knoop, krijgen een tijdelijk label. 'Knopen met een tijdelijk label krijgen enkel een nieuw tijdelijk 'label als de distance-waarde van dat nieuwe label kleiner is dan 'die van het huidige label. Dim j As Integer 'teller Dim rij As Integer Dim kolom As Integer 'Parameters om het rij- en kolomnummer bij te houden. rij = xlWs.Range("netwerk").row + i kolom = xlWs.Range("netwerk").column
Appendix B: Code
190
'Rooster met de verbindingen doorlopen. For j = 1 To dc.getCheckpoints() rij = rij kolom = kolom + 1 'Onder de diagonaal van het rooster. If rij < kolom Then If xlWs.Cells(rij, kolom).Value <> "" Then If knopen.Item(kolom - 1).getStatus = 0 Then Call knopen.Item(kolom - 1).setStatus(1) Call knopen.Item(kolom - 1).setPrevious(i) Call knopen.Item(kolom - 1).setDistance(knopen.Item _ (i).getDistance + xlWs.Cells(rij, kolom).Value) Else If knopen.Item(kolom - 1).getStatus = 1 Then If knopen.Item(i).getDistance + xlWs.Cells(rij, kolom) _ < knopen.Item(kolom - 1).getDistance Then Call knopen.Item(kolom - 1).setStatus(1) Call knopen.Item(kolom - 1).setPrevious(i) Call knopen.Item(kolom - 1).setDistance(knopen.Item _ (i).getDistance + xlWs.Cells(rij, kolom).Value) End If End If End If End If End If 'Boven de diagonaal van het rooster. If rij > kolom Then xlWs.Cells(rij, kolom).Value = xlWs.Cells(kolom, rij).Value 'Indexen wisselen om dezelfde reden als bij First_iteration. If xlWs.Cells(rij, kolom).Value <> "" Then If knopen.Item(kolom - 1).getStatus = 0 Then Call knopen.Item(kolom - 1).setStatus(1) Call knopen.Item(kolom - 1).setPrevious(i) Call knopen.Item(kolom - 1).setDistance(knopen.Item _ (i).getDistance + xlWs.Cells(rij, kolom).Value) Else If knopen.Item(kolom - 1).getStatus = 1 Then If knopen.Item(i).getDistance + xlWs.Cells(rij, kolom) _ < knopen.Item(kolom - 1).getDistance Then Call knopen.Item(kolom - 1).setStatus(1) Call knopen.Item(kolom - 1).setPrevious(i) Call knopen.Item(kolom - 1).setDistance(knopen.Item _ (i).getDistance + xlWs.Cells(rij, kolom).Value) End If End If End If End If End If Next j End Sub ________________________________________________________________________________ Private Sub Scheme()
Appendix B: Code
191
'De resultaten van Dijkstra's algoritme wegschrijven naar de schema'sheets van de Excel-file die als invoerbestand fungeert. Dim m As Integer 'teller Dim rij As Integer Dim kolom As Integer 'Parameters om het rij- en kolomnummer bij te houden. rij = xlWs.Range("schema" & l).row kolom = xlWs.Range("schema" & l).column xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij, xlWs.Cells(rij,
kolom kolom kolom kolom kolom kolom
+ + + + + +
1) 2) 3) 4) 5) 6)
= = = = = =
"Nummer" "X" "Y" "Status" "Previous" "Distance"
For m = 1 To dc.getCheckpoints() xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij xlWs.Cells(rij
+ + + + + +
m, m, m, m, m, m,
kolom kolom kolom kolom kolom kolom
+ + + + + +
1).Value 2).Value 3).Value 4).Value 5).Value 6).Value
= = = = = =
knopen.Item(m).getNummer knopen.Item(m).getX knopen.Item(m).getY knopen.Item(m).getStatus knopen.Item(m).getPrevious knopen.Item(m).getDistance
Next m End Sub ________________________________________________________________________________ Private Sub Collection_initialiseren() 'De begineigenschappen van de knopen instellen. Dim i As Integer For i = 1 To dc.getCheckpoints() Dim checkpoint As Knoop Set checkpoint = New Knoop knopen.Add checkpoint Call Call Call Call Call Call Call Call
knopen.Item(i).setNummer(dc.getCheckpointlijst(i, 0)) knopen.Item(i).setX(dc.getCheckpointlijst(i, 1)) knopen.Item(i).setY(dc.getCheckpointlijst(i, 2)) knopen.Item(i).setRij(dc.getCheckpointlijst(i, 3)) knopen.Item(i).setKolom(dc.getCheckpointlijst(i, 4)) knopen.Item(i).setStatus(0) knopen.Item(i).setPrevious(0) knopen.Item(i).setDistance(0)
Next i End Sub ________________________________________________________________________________ Private Sub Kortste_route() 'De kortste route bepalen met behulp van Dijkstra's algoritme. 'Werkwijze: 'Voor elke mogelijke startknoop bepalen we de afstand tussen de
Appendix B: Code
192
'startlocatie en die knoop en voeren we Dijkstra's algoritme uit. 'De resultaten van elke uitvoering schrijven we in de schema-sheets 'de inputfile. Vervolgens bepalen voor de combinaties van de startknoop 'met elke mogelijke eindknoop, de afstand tussen de twee knopen en 'de afstand tussen de eindknoop en de doellocatie. 'Dan halen we uit alle mogelijke routes die met de kortste 'afstand. Aan de hand van de start- en eindknoop bepalen we dan 'alle knopen die deel uitmaken van de kortste route. Dim i As Integer Dim j As Integer 'tellers Dim rij As Integer Dim kolom As Integer 'Parameters om het rij - en kolomnummer bij te houden. For l = 1 To routingalgoritme.getAantalStartknopen() Collection_initialiseren Afstand_tussen_startlocatie_en_startknoop Dijkstra (routingalgoritme.getStartknopen(l)) xlApp.Worksheets("schema" & l).Activate Set xlWs = xlWb.ActiveSheet Scheme rij = xlWs.Cells.Range("schema" & l).row kolom = xlWs.Cells.Range("schema" & l).column For m = 1 To routingalgoritme.getAantalEindknopen() Call routingalgoritme.setDeelafstand(2, xlWs.Cells(rij _ + routingalgoritme.getEindknopen(m), kolom + 6).Value) Afstand_tussen_doellocatie_en_eindknoop Call routingalgoritme.setAfstand(l, m, _ routingalgoritme.getDeelafstand(1) _ + routingalgoritme.getDeelafstand(2) _ + routingalgoritme.getDeelafstand(3)) Next m Next l Bepaling_kortste_route Knopen_kortste_route End Sub ________________________________________________________________________________ Private Sub Afstand_tussen_startlocatie_en_startknoop() 'De x-coördinaten van startlocatie en startknoop zijn gelijk. If weg.getStartLocatie(3) = _ knopen.Item(routingalgoritme.getStartknopen(l)).getX Then Call routingalgoritme.setDeelafstand(1, _ Abs(weg.getStartLocatie(4) _ - knopen.Item(routingalgoritme.getStartknopen(l)).getY)) End If 'De afstand is de absolute waarde van het verschil in y-coördinaten. 'De y-coördinaten van startlocatie en startknoop zijn gelijk. If weg.getStartLocatie(4) = _ knopen.Item(routingalgoritme.getStartknopen(l)).getY Then Call routingalgoritme.setDeelafstand(1, _
Appendix B: Code
193
Abs(weg.getStartLocatie(3) _ - knopen.Item(routingalgoritme.getStartknopen(l)).getX)) End If 'De afstand is de absolute waarde van het verschil in x-coördinaten. End Sub ________________________________________________________________________________ Private Sub Afstand_tussen_doellocatie_en_eindknoop() 'De x- coördinaten van doellocatie en doelknoop zijn gelijk. If weg.getDoelLocatie(3) = _ knopen.Item(routingalgoritme.getEindknopen(m)).getX Then Call routingalgoritme.setDeelafstand(3, _ Abs(weg.getDoelLocatie(4) _ - knopen.Item(routingalgoritme.getEindknopen(m)).getY)) End If 'De afstand is de absolute waarde van het verschil in y-coôrdinaten. 'De y-coördinaten van doellocatie en doelknoop zijn gelijk. If weg.getDoelLocatie(4) = _ knopen.Item(routingalgoritme.getEindknopen(m)).getY Then Call routingalgoritme.setDeelafstand(3, _ Abs(weg.getDoelLocatie(3) _ - knopen.Item(routingalgoritme.getEindknopen(m)).getX)) End If 'De afstand is de absolute waarde van het verschil in x-coördinaten. End Sub ________________________________________________________________________________ Private Sub Bepaling_kortste_route() 'Uit alle mogelijke combinaties van start- en eindknopen deze 'selecteren met de kortste route. 'Tegelijk hebben we dan de beginknoop en de eindknoop van de 'kortste pad bepaald. Dim i As Integer Dim j As Integer 'tellers Call routingalgoritme.setBeginKnoop(routingalgoritme.getStartknopen(1)) Call routingalgoritme.setEindKnoop(routingalgoritme.getEindknopen(1)) start = 1 eind = 1 For i = 1 To routingalgoritme.getAantalStartknopen() For j = 1 To routingalgoritme.getAantalEindknopen() If routingalgoritme.getAfstand(i, j) < _ routingalgoritme.getAfstand(start, eind) Then start = i eind = j Call routingalgoritme.setBeginKnoop(routingalgoritme.getStartknopen(start)) Call routingalgoritme.setEindKnoop(routingalgoritme.getEindknopen(eind)) End If Next j Next i 'Initieel wordt de eerste combinatie als beste genomen. Daarna
Appendix B: Code
194
'bekijken we de overige combinaties waarbij de waarde van de 'beste wordt gewijzigd van zodra één van deze combinaties een 'kortere route heeft. Call outputdata.setAfstandlijst(w, o, r, routingalgoritme.getAfstand(start, _ eind)) End Sub ________________________________________________________________________________ Private Sub Knopen_kortste_route() 'Alle knopen van het kortste pad bepalen. 'Werkwijze: 'We gaan naar de sheet met de resultaten van de Dijkstra's 'algoritme uitgevoerd met de startknoop van de kortste route 'als beginknoop. We vertrekken in dat schema van de rij die 'overeenkomt met de eindknoop van de kortste route. 'Dan lezen we het nummer van de vorige knoop in het kortste pad '(vijfde kolom van het schema) en gaan we naar de rij die met 'deze knoop overeenstemt. Daar lezen we opnieuw het nummer van 'de vorige knoop. Deze laatste operaties blijven we uitvoeren 'tot we op de rij belanden die overeenkomt met de startknoop. Dim i As Integer 'teller Dim rij As Integer Dim kolom As Integer 'Parameters om het rij- en kolomnummer bij te houden. xlApp.Worksheets("schema" & start).Activate Set xlWs = xlWb.ActiveSheet rij = xlWs.Cells.Range("schema" & start).row kolom = xlWs.Cells.Range("schema" & start).column Call routingalgoritme.setAantalKnopen(0) Call routingalgoritme.setAantalKnopen(routingalgoritme.getAantalKnopen() + 1) Call routingalgoritme.setKortstePad(routingalgoritme.getAantalKnopen, _ routingalgoritme.getEindKnoop) rij = rij + routingalgoritme.getEindKnoop() Do Until rij = xlWs.Cells.Range("schema" & start).row + _ routingalgoritme.getBeginKnoop() Call routingalgoritme.setAantalKnopen(routingalgoritme.getAantalKnopen() _ + 1) Call routingalgoritme.setKortstePad(routingalgoritme.getAantalKnopen, _ xlWs.Cells(rij, kolom + 5)) rij = xlWs.Cells.Range("schema" & start).row + xlWs.Cells(rij, kolom + 5) Loop End Sub ________________________________________________________________________________ Private Sub Route_vastleggen() 'De opeenvolgende routes afgelegd in het magazijn wegschrijven 'naar de lijst route. Call Call Call Call
weg.setRoute(weg.getRoutenummer, weg.setRoute(weg.getRoutenummer, weg.setRoute(weg.getRoutenummer, weg.setRoute(weg.getRoutenummer,
Appendix B: Code
weg.getStap, weg.getStap, weg.getStap, weg.getStap,
1, 2, 3, 4,
w) o) r) weg.getStap)
195
Call Call Call Call
weg.setRoute(weg.getRoutenummer, weg.setRoute(weg.getRoutenummer, weg.setRoute(weg.getRoutenummer, weg.setRoute(weg.getRoutenummer,
weg.getStap, weg.getStap, weg.getStap, weg.getStap,
5, 6, 7, 8,
dc.getMatrix(a, b, 1)) dc.getMatrix(a, b, 2)) a) b)
End Sub ________________________________________________________________________________ Private Sub Cel_zoeken(c As Integer, d As Integer) 'Procedure gebruikt om de route te reconstrueren. 'Als argumenten geven we het rij- en kolomnummer mee van een 'knoop of de doellocatie, naargelang het deel van de route 'dat we aan het reconstrueren zijn. 'rijnummer gelijk aan argument If a = c Then If b <> d Then If b < d Then Call Cel_rechts(c, d) Else Call Cel_links(c, d) End If End If End If 'kolomnummer gelijk aan argument If b = d Then If a <> c Then If a < c Then Call Cel_onder(c, d) Else Call Cel_boven(c, d) End If End If End If End Sub ________________________________________________________________________________ Private Sub Cel_rechts(c As Integer, d As Integer) 'Een cel naar rechts bewegen. 'Eerst controleren we of we niet in de laatste kolom van het rooster 'zitten en of de cel rechts wel deel uitmaakt van een gang. If Not b = dc.getKolommen() Then If dc.getMatrix(a, b + 1, 3) = 0 Then a = a b = b + 1 Debug.Print "cel (" & a & "," & b & ") " End If End If Call dc.setMatrix(a, b, 4, 3) 'Stapnummer met één verhogen en de celgegevens in de lijst route 'schrijven. Call weg.setStap(weg.getStap + 1) Route_vastleggen 'Controle of de gewenste plaats (gespecifieerd door de argumenten) 'al bereikt is. Indien dit het geval is krijgt de Boolse variabele 'reachcheck de waarde true.
Appendix B: Code
196
If a = c Then If b = d Then reachcheck = True End If End If End Sub ________________________________________________________________________________ Private Sub Cel_links(c As Integer, d As Integer) 'Een cel naar links bewegen. 'Eerst controleren we of we niet in de eerste kolom van het rooster 'zitten en of de cel links wel deel uitmaakt van een gang. If Not b = 1 Then If dc.getMatrix(a, b - 1, 3) = 0 Then a = a b = b - 1 Debug.Print "cel (" & a & "," & b & ") " End If End If Call dc.setMatrix(a, b, 4, 3) 'Stapnummer met één verhogen en de celgegevens in de lijst route 'schrijven. Call weg.setStap(weg.getStap + 1) Route_vastleggen 'Controle of de gewenste plaats (gespecifieerd door de argumenten) 'al bereikt is. Indien dit het geval is krijgt de Boolse variabele 'reachcheck de waarde true. If a = c Then If b = d Then reachcheck = True End If End If End Sub ________________________________________________________________________________ Private Sub Cel_onder(c As Integer, d As Integer) 'Een cel naar onder bewegen. 'Eerst controleren we of we niet in de laatste rij van het rooster 'zitten en of de cel onder wel deel uitmaakt van een gang. If Not a = dc.getRijen() Then If dc.getMatrix(a + 1, b, 3) = 0 Then a = a + 1 b = b Debug.Print "cel (" & a & "," & b & ") " End If End If Call dc.setMatrix(a, b, 4, 3) 'Stapnummer met één verhogen en de celgegevens in de lijst route 'schrijven. Call weg.setStap(weg.getStap + 1) Route_vastleggen 'Controle of de gewenste plaats (gespecifieerd door de argumenten) 'al bereikt is. Indien dit het geval is krijgt de Boolse variabele
Appendix B: Code
197
'reachcheck de waarde true. If a = c Then If b = d Then reachcheck = True End If End If End Sub ________________________________________________________________________________ Private Sub Cel_boven(c As Integer, d As Integer) 'Een cel naar boven bewegen. 'Eerst controleren we of we niet in de eerste rij van het rooster 'zitten en of de cel onder wel deel uitmaakt van een gang. If Not a = 1 Then If dc.getMatrix(a - 1, b, 3) = 0 Then a = a - 1 b = b Debug.Print "cel (" & a & "," & b & ") " End If End If Call dc.setMatrix(a, b, 4, 3) 'Stapnummer met één verhogen en de celgegevens in de lijst route 'schrijven. Call weg.setStap(weg.getStap + 1) Route_vastleggen
'Controle of de gewenste plaats (gespecifieerd door de argumenten) 'al bereikt is. Indien dit het geval is krijgt de Boolse variabele 'reachcheck de waarde true. If a = c Then If b = d Then reachcheck = True End If End If End Sub ________________________________________________________________________________ Private Sub Layout_initialiseren() 'De layout weergeven zonder aanduiding van de knopen. Dim m As Integer Dim n As Integer 'teller For m = 1 To dc.getRijen() For n = 1 To dc.getKolommen() If dc.getMatrix(m, n, 4) = 3 Then If dc.getMatrix(m, n, 1) = dc.getCAP(1) Then If dc.getMatrix(m, n, 2) = dc.getCAP(2) Then Call dc.setMatrix(m, n, 4, 2) Else Call dc.setMatrix(m, n, 4, 0) End If Else Call dc.setMatrix(m, n, 4, 0) End If End If
Appendix B: Code
198
Next n Next m End Sub ________________________________________________________________________________ Private Sub Rekken_en_pickplaatsen_kleuren() 'De rekken waaruit gepickt moet worden en de bijhorende pickplaatsen 'doen oplichten. Dim i As Dim j As Dim k As Dim m As Dim n As 'tellers
Integer Integer Integer Integer Integer
For i = 1 To picklijst.getOrders(w) For j = 1 To picklijst.getOrderregels(w, i) 'De pickplaatsen. Call dc.setMatrix(weg.getPickplaats(w, i, j, 3), _ weg.getPickplaats(w, i, j, 4), 4, 5) 'De rekken. For k = 1 To dc.getAantalRekken() If picklijst.getOrderlijst(w, i, j, 3) = _ dc.getReklijst(k, 1) Then For m = 1 To dc.getRijen() For n = 1 To dc.getKolommen() If dc.getMatrix(m, n, 1) = _ dc.getReklijst(k, 2) Then If dc.getMatrix(m, n, 2) = _ dc.getReklijst(k, 3) Then Call dc.setMatrix(m, n, 4, 4) End If End If Next n Next m End If Next k Next j Next i End Sub ________________________________________________________________________________ Private Sub Pad_initialiseren() 'Bij elke nieuwe orderregel het pad initialiseren, d.w.z. cellen die 'een gang zijn opnieuw als een gang voorstellen en de pickplaatsen 'van de betreffende wave opnieuw doen opichten. 'Werkwijze: voor alle cellen waar matrix(m,n,4) = 3, wat overeen'komt met een cel van het pad, deze waarde opnieuw gelijk 0 stel'len tenzij de cel overeenkomt met het CAP of een pickplaats die 'moet aangedaan worden gedurende de pickronde die men aan het af'handelen is. Dim Dim Dim Dim
i j m n
As As As As
Integer Integer Integer Integer
Appendix B: Code
199
Dim order As Integer 'tellers order = o For m = 1 To dc.getRijen() For n = 1 To dc.getKolommen() If dc.getMatrix(m, n, 4) = 3 Then If dc.getMatrix(m, n, 1) = dc.getCAP(1) Then If dc.getMatrix(m, n, 2) = dc.getCAP(2) Then Call dc.setMatrix(m, n, 4, 2) Else For i = order To picklijst.getOrders(w) For j = 1 To picklijst.getOrderregels(w, i) If dc.getMatrix(m, n, 1) = _ weg.getPickplaats(w, i, j, 1) Then If dc.getMatrix(m, n, 2) = _ weg.getPickplaats(w, i, j, 2) Then If i = order Then If j >= r Then Call dc.setMatrix(m, n, 4, 5) Else Call dc.setMatrix(m, n, 4, 0) End If Else Call dc.setMatrix(m, n, 4, 5) End If Else If Not dc.getMatrix(m, n, 4) = 5 Then Call dc.setMatrix(m, n, 4, 0) End If End If Else If Not dc.getMatrix(m, n, 4) = 5 Then Call dc.setMatrix(m, n, 4, 0) End If End If Next j Next i End If Else For i = order To picklijst.getOrders(w) For j = 1 To picklijst.getOrderregels(w, i) If dc.getMatrix(m, n, 1) = _ weg.getPickplaats(w, i, j, 1) Then If dc.getMatrix(m, n, 2) = _ weg.getPickplaats(w, i, j, 2) Then If i = order Then If j >= r Then Call dc.setMatrix(m, n, 4, 5) Else Call dc.setMatrix(m, n, 4, 0) End If Else Call dc.setMatrix(m, n, 4, 5) End If Else If Not dc.getMatrix(m, n, 4) = 5 Then Call dc.setMatrix(m, n, 4, 0) End If
Appendix B: Code
200
End If Else If Not dc.getMatrix(m, n, 4) = 5 Then Call dc.setMatrix(m, n, 4, 0) End If End If Next j Next i End If End If Next n Next m End Sub ________________________________________________________________________________ Private Sub Meerdere_keren_picken_uit_een_rek() 'Een rek waaruit reeds gepickt werd maar waaruit nog eens moet 'gepickt worden gedurende de pickronde die we aan het afhandelen 'zijn opnieuw doen oplichten. Dim i As Integer Dim j As Integer Dim k As Integer Dim m As Integer Dim n As Integer Dim order As Integer 'tellers order = o For i = order To picklijst.getOrders(w) For j = 1 To picklijst.getOrderregels(w, i) For k = 1 To dc.getAantalRekken() If picklijst.getOrderlijst(w, i, j, 3) = dc.getReklijst(k, 1) Then For m = 1 To dc.getRijen() For n = 1 To dc.getKolommen() If dc.getMatrix(m, n, 1) = dc.getReklijst(k, 2) Then If dc.getMatrix(m, n, 2) = dc.getReklijst(k, 3) Then If i = order Then If j >= r Then Call dc.setMatrix(m, n, 4, 4) End If Else Call dc.setMatrix(m, n, 4, 4) End If End If End If Next n Next m End If Next k Next j Next i End Sub ________________________________________________________________________________ Private Sub Rek_na_picken() 'Rekken waaruit de gevraagde hoeveelheid gepickt werd een andere 'andere kleur geven en de inhoud van dat rek verminderen met die hoeveelheid.
Appendix B: Code
201
Dim k As Integer Dim m As Integer Dim n As Integer 'tellers For k = 1 To dc.getAantalRekken() If picklijst.getOrderlijst(w, o, r, 3) = dc.getReklijst(k, 1) Then Call dc.setReklijst(k, 7, (dc.getReklijst(k, 7) - picklijst.getOrderlijst(w, o, r, 2))) If inputdata.getDialoog = vbYes Then MsgBox "In rek nr " & dc.getReklijst(k, 1) & "bevinden zich nog" &_ dc.getReklijst(k, 7) & " eenheden van product nr " & _ dc.getReklijst(k, 6) End If For m = 1 To dc.getRijen() For n = 1 To dc.getKolommen() If dc.getMatrix(m, n, 1) = dc.getReklijst(k, 2) Then If dc.getMatrix(m, n, 2) = dc.getReklijst(k, 3) Then Call dc.setMatrix(m, n, 4, 6) End If End If Next n Next m End If Next k End Sub ________________________________________________________________________________ Private Sub Startknopen_en_eindknopen_en_kortstepad_initialiseren() 'De lijsten met start- en eindknopen en de knopen van het kortste 'pad initialiseren. Dim m As Integer 'teller For m = 1 To maxknopen Call routingalgoritme.setStartknopen(m, 0) Call routingalgoritme.setEindknopen(m, 0) Call routingalgoritme.setKortstePad(m, 0) Next m End Sub ________________________________________________________________________________ Private Sub Kortste_route_reconstrueren() 'De gegevens van de kortste route bepalen, door de opeenvolgende 'cellen te zoeken die de verschillende delen van de kortste route 'samenstellen. Dim i As Integer 'teller 'Het stuk vanaf de startlocatie tot de beginknoop. Call weg.setStap(0) Call dc.setMatrix(weg.getStartLocatie(1), _ weg.getStartLocatie(2), 4, 3) a = weg.getStartLocatie(1) b = weg.getStartLocatie(2) Route_vastleggen
Appendix B: Code
202
reachcheck = False Do While Not reachcheck Call Cel_zoeken(knopen.Item(routingalgoritme.getKortstePad _ (routingalgoritme.getAantalKnopen())).getRij, _ knopen.Item(routingalgoritme.getKortstePad _ (routingalgoritme.getAantalKnopen())).getKolom) Loop 'Het stuk tussen de start- en eindknoop. For i = routingalgoritme.getAantalKnopen - 1 To 1 Step -1 reachcheck = False Do While Not reachcheck Call Cel_zoeken(knopen.Item(routingalgoritme.getKortstePad(i)).getRij, _ knopen.Item(routingalgoritme.getKortstePad(i)).getKolom) Loop Next i 'Het stuk van de eindknoop tot de doellocatie. reachcheck = False Do While Not reachcheck Call Cel_zoeken(weg.getDoelLocatie(1), weg.getDoelLocatie(2)) Loop
End Sub ________________________________________________________________________________ Private Sub Totale_transporttijd_picktijd_en_afstand() 'De totale en cumulatieve afstand, picktijd en transporttijd 'berekenen. 'de afstand Call outputdata.setTotaalAfstand(w, outputdata.getTotaalAfstand(w) + _ outputdata.getAfstandlijst(w, o, r)) Call outputdata.setCumulatieveAfstand(w, o, r, _ outputdata.getTotaalAfstand(w)) 'de transporttijd Call outputdata.setTotaalTransporttijd(w, outputdata.getTotaalTransporttijd(w)+_ outputdata.getTransporttijdlijst(w, o, r)) Call outputdata.setCumulatieveTransporttijd(w, o, r, _ outputdata.getTotaalTransporttijd(w)) 'de picktijd Call outputdata.setTotaalPicktijd(w, outputdata.getTotaalPicktijd(w) + _ outputdata.getPicktijdlijst(w, o, r)) Call outputdata.setCumulatievePicktijd(w, o, r, _ outputdata.getTotaalPicktijd(w))
If inputdata.getDialoog() = vbYes Then MsgBox " De cumulatieve afstand van pickronde " & w & " bedraagt " & _ Round(outputdata.getTotaalAfstand(w), 2) & " m." & Chr(13) & _ " De cumulatieve transporttijd van pickronde " & w & " bedraagt" & _ Round(outputdata.getTotaalTransporttijd(w), 2) & " s." & Chr(13) & _ " De cumulatieve picktijd van pickronde " & w & " bedraagt " & _ Round(outputdata.getTotaalPicktijd(w), 2) & " s."
Appendix B: Code
203
End If
End Sub ________________________________________________________________________________ Private Sub Transporttijd_en_picktijd() 'De transporttijd en picktijd per orderregel berekenen. 'Gaat het om het laatste order van een wave dan is de picktijd 'gelijk aan de transporttijd (afstand/snelheid). 'In alle andere gevallen is de picktijd gelijk aan de transport'tijd vermeerderd met de gemiddelde picktijd. If o = picklijst.getOrders(w) + 1 Then Call outputdata.setPicktijdlijst(w, o, r, _ outputdata.getAfstandlijst(w, o, r) / dc.getSnelheid()) Else Call outputdata.setPicktijdlijst(w, o, r, _ outputdata.getAfstandlijst(w, o, r) / dc.getSnelheid _ + dc.getPicktijd()) End If Call outputdata.setTransporttijdlijst(w, o, r, _ outputdata.getAfstandlijst(w, o, r) / dc.getSnelheid)
If inputdata.getDialoog() = vbYes Then If o = picklijst.getOrders(w) + 1 Then MsgBox " De afstand afgelegd om het centraal afgiftepunt te bereiken " _ & " bedraagt "&Round(outputdata.getAfstandlijst(w, o, r), 2)&" m." _ & Chr(13) &"De tijd nodig om zich naar het centraal afgiftepunt te"_ & "begeven bedraagt"&Round(outputdata.getPicktijdlijst(w, o, r), 2) & " s." Else MsgBox " De afstand afgelegd voor het afhandelen van de orderregel" _ & " bedraagt " & Round(outputdata.getAfstandlijst(w, o, r), 2)&"m."_ & Chr(13) & " De tijd nodig voor het afhandelen van de orderregel" _ & " bedraagt " & Round(outputdata.getPicktijdlijst(w, o, r),2)&"s."_ & Chr(13) & " De tijd ingenomen door transport bedraagt daarbij "& _ Round(outputdata.getTransporttijdlijst(w, o, r), 2) & " s." End If End If End Sub ________________________________________________________________________________ Private Sub Picken() 'Het orderverzamelproces uitvoeren. Call weg.setRoutenummer(0) For w = 1 To picklijst.getWaves() If inputdata.getDialoog() = vbYes Then MsgBox " verzamelronde " & w End If Rekken_en_pickplaatsen_kleuren Form_paint Call outputdata.setTotaalAfstand(w, 0) Call outputdata.setTotaalPicktijd(w, 0)
Appendix B: Code
204
Call outputdata.setTotaalTransporttijd(w, 0) For o = 1 To picklijst.getOrders(w) + 1 If inputdata.getDialoog() = vbYes Then If Not o = picklijst.getOrders(w) + 1 Then MsgBox " order " & o & " van verzamelronde " & w End If End If
For r = 1 To picklijst.getOrderregels(w, o) If inputdata.getDialoog() = vbYes Then If Not o = picklijst.getOrders(w) + 1 Then MsgBox " orderregel " & r & " van " & picklijst.getOrderregels(w, o) End If End If Call weg.setRoutenummer(weg.getRoutenummer + 1) Me.Show picLayout.Cls Form_paint form_resize Pad_initialiseren Meerdere_keren_picken_uit_een_rek Form_paint startlocatie doellocatie Form_paint form_resize Dijkstra_check Dubbele_gang_check If Not doubleaislecheck Then If dijkstracheck = False Then Kortste_pad_zonder_Dijkstra_enkele_gang End If If dijkstracheck = True Then Kortste_pad_met_Dijkstra End If End If Transporttijd_en_picktijd Totale_transporttijd_picktijd_en_afstand Rek_na_picken Form_paint form_resize Call weg.setAantalStappen(weg.getRoutenummer, weg.getStap) Call Sleep(inputdata.getDelay()) Next r Pad_initialiseren Next o Pad_initialiseren Next w End Sub ________________________________________________________________________________
Appendix B: Code
205
Private Sub Form_Load() Intro.Show Modal:=vbModal Set inputdata = New Invoer Set dc = New Magazijn Call dc.init(inputdata) ''of dc.init data dc.setInvoer inputdata ''of call dc.setinvoer(data) Set picklijst = New order Call picklijst.init(inputdata) Call picklijst.setInvoer(inputdata) Set weg = New Route Call weg.init(dc, picklijst) Call weg.setMagazijn(dc) Call weg.setOrder(picklijst) Set routingalgoritme = New Dijkstra Set outputdata = New Uitvoer Set knopen = New Collection Collection_initialiseren Form_paint Me.Show Sleep (inputdata.getDelay()) MsgBox "Klik OK om de orderpicking te starten." Layout_initialiseren Form_paint Me.Show Sleep (inputdata.getDelay()) Set xlApp = New Excel.Application xlApp.Visible = False Set xlWb = xlApp.Workbooks.Open(inputdata.getBestandsnaam()) Picken xlApp.Visible = True xlApp.Quit Call outputdata.init(dc, picklijst, weg) Set knopen = Nothing End Sub Private Sub form_resize() Form_paint End Sub
Appendix B: Code
206
Bibliografische Referenties
[1]
AGHEZZAF, E.H. (2002). Network Models & Project Scheduling: PERT/CPM (cursusnota’s). Gent, Faculteit Toegepaste Wetenschappen, Universiteit Gent, 14p.
[2]
DE KOSTER, M.B.M. (1995). Lay-out ontwerp. In: DUIJKER, J.P. & DE KOSTER, M.B.M. & PLOOS VAN AMSTEL, M.J. (1995). Praktijkboek Magazijnen en Distributiecentra. Deventer, Kluwer, 3.3A
[3]
DE KOSTER, M.B.M. (1995). Rijdend intern transportmaterieel. In: DUIJKER, J.P. & DE KOSTER, M.B.M. & PLOOS VAN AMSTEL, M.J. (1995). Praktijkboek Magazijnen en Distributiecentra. Deventer, Kluwer, 3.7A
[4]
DE KOSTER, M.BM. & ROODBERGEN, K.J. (1995). Een magazijn ontwerpen op internet. In: DUIJKER, J.P. & DE KOSTER, M.B.M. & PLOOS VAN AMSTEL, M.J. (1995). Praktijkboek Magazijnen en Distributiecentra. Deventer, Kluwer, 3.3F
[5]
ENGINEERING MEMORIAL UNIVERSITY. (electronische copie, URL: http://www.engr.mun.ca)
[6]
GEENEN, A.L.J. & PLOOS VAN AMSTEL, M.J. (1995). Dynamiek rondom warehousing. In: DUIJKER, J.P. & DE KOSTER, M.B.M. & PLOOS VAN AMSTEL, M.J. (1995). Praktijkboek Magazijnen en Distributiecentra. Deventer, Kluwer, 1.2
[7]
GELDERS, L. & PINTELON, L. (1995). Organisatie van het logistieke en magazijnproces. In: DUIJKER, J.P. & DE KOSTER, M.B.M. & PLOOS VAN AMSTEL, M.J. (1995). Praktijkboek Magazijnen en Distributiecentra. Deventer, Kluwer, 3.5A
[8]
HAGDORN-VAN DER MEIJDEN, L. & VAN NUNEN, J.A.E.E. (1995). Informatie- en communicatietechnologie en de rol van het distributiecentrum in vraaggestuurde netwerken. In: DUIJKER, J.P. & DE KOSTER, M.B.M. & PLOOS VAN AMSTEL, M.J. (1995). Praktijkboek Magazijnen en Distributiecentra. Deventer, Kluwer, 1.4A
[9]
HEREIJGERS, E. (1995). Opslagsystemen. In: DUIJKER, J.P. & DE KOSTER, M.B.M. & PLOOS VAN AMSTEL, M.J. (1995). Praktijkboek Magazijnen en Distributiecentra. Deventer, Kluwer, 3.6
[10]
MONADJEMI, P. (2000). Snel leren programmeren Visual Basic. München, Pearson Education BV, 514 p.
[11]
MSDN.MICROSOFT.COM. (electronische copie, URL: http://msdn.microsoft.com)
Bibliografische referenties
207
[12]
PLOOS VAN AMSTEL, M.J. & VERDUIJN, T.M. (1995). De gevolgen van logistieke ondernemerskeuzen voor de functie, inrichting en besturing van ditributiecentra. In: DUIJKER, J.P. & DE KOSTER, M.B.M. & PLOOS VAN AMSTEL, M.J. (1995). Praktijkboek Magazijnen en Distributiecentra. Deventer, Kluwer, 1.3
[13]
POST, H.N. (2003). Transport, Routing- en Schedulingproblemen. (electronische copie, URL: http://ssor.twi.tudelft.nl/~roos/courses/wi487tu/main.pdf)
[14]
SHARP, G. & KRUEGER, K.W. A simulation software tool for order picking in a person-aboard storage/retrieval system.
[15]
SPEE, D. (1995). Middelen: combinaties van opslag en transportmaterieel met besturing. In: DUIJKER, J.P. & DE KOSTER, M.B.M. & PLOOS VAN AMSTEL, M.J. (1995). Praktijkboek Magazijnen en Distributiecentra. Deventer, Kluwer, 3.8
[16]
STICHTING LOGISTICA. (electronische copie, URL: http://www.fbk.ur.nl/OZ/LOGISTICA)
[17]
TOGETHERSOFT.COM (electronische copie, URL: http://www.togehtersoft.com)
[18]
TOMPKINS, J.A. & WHITE, J.A. (1984). Facilities Planning. New York, John Wiley & Sons Inc, 675 p.
[19]
VAN DER LINDEN, J.P.H. & HEUTS, R.M.J. (1995). Simulatie als toolbox voor magazijnontwerp. In: DUIJKER, J.P. & DE KOSTER, M.B.M. & PLOOS VAN AMSTEL, M.J. (1995). Praktijkboek Magazijnen/Distributiecentra. Deventer, Kluwer, 3.5C
[20]
VAN LANDEGHEM, R. (2003). Conceptie van magazijnen en distributiecentra (cursusnota’s). Gent, Faculteit Toegepaste Wetenschappen, Universiteit Gent, 40p.
[21]
W3.ORG. (electronische copie, URL: http://www.w3.org/TR/REC-DOMLevel1/introduction.html)
[22]
WEB.CERN (electronische copie, URL: http://harp.web.cern.ch)
Bibliografische referenties
208