/// <summary> /// Sets the end node of the edge /// </summary> /// <param name="p_node">New end node</param> public void setEnd(node p_node) { end = p_node; }
/// <summary> /// /// </summary> /// <param name="p_skus">List of SKUs</param> /// <param name="p_wh">Warehouse object</param> /// <param name="method">Allocation method: 0 turnover, 1 straight, 2 random</param> /// <returns>Returns -1 if there is insufficent warehouse space, 0 if there is sufficent warehouse space</returns> public int allocateSKUs(List <sku> p_skus, warehouse p_wh, int method) { int totalstoragelocations = p_wh.totalNumberOfLocations(); if (p_skus.Count > totalstoragelocations) { return(-1); //Return insufficient warehouse space } //Add warehouse locations to locations list in allocation class for (int i = 0; i < p_wh.regions.Count; i++) { for (int j = 0; j < p_wh.regions[i].pickingaisleedges.Count(); j++) { for (int k = 0; k < p_wh.regions[i].pickingaisleedges[j].getOnEdgeNodes().Count(); k++) { locations.Add(p_wh.regions[i].pickingaisleedges[j].getOnEdgeNodes()[k]); } } } //Use a different sku list to sort the skus based on their frequency //We don't want to change the order of skus in the original list because this might affect the randomized allocation process later for (int i = 0; i < p_skus.Count; i++) { sortedskus.Add(p_skus[i]); } if (method == 0)//Turnover based storage allocation { sortedskus.Sort((x, y) => y.pickprobability.CompareTo(x.pickprobability)); //Set sku locations based on their frequency to the best locations based on overall ranking int k = 0; for (int i = 0; i < sortedskus.Count; i++) { bool added = false; if (p_wh.overallranking[k].node1.s1 != null) { if (p_wh.overallranking[k].node1.s1.addSKU(sortedskus[i])) { sortedskus[i].location = p_wh.overallranking[k].node1; added = true; } } if (!added && p_wh.overallranking[k].node1.s2 != null) { if (p_wh.overallranking[k].node1.s2.addSKU(sortedskus[i])) { sortedskus[i].location = p_wh.overallranking[k].node1; added = true; } } if (!added) { k++; i--; } } } else if (method == 1) //Straight allocation (first SKU goes to first location based on location ID) { int k = 0; for (int i = 0; i < sortedskus.Count; i++) { bool added = false; if (locations[k].s1 != null) { if (locations[k].s1.addSKU(sortedskus[i])) { sortedskus[i].location = locations[k]; added = true; } } if (!added && locations[k].s2 != null) { if (locations[k].s2.addSKU(sortedskus[i])) { sortedskus[i].location = locations[k]; added = true; } } if (!added) { k++; i--; } } } else //Randomized allocation, each SKU goes to a random location { int n = 2;//Number of SKUs per pick location while (sortedskus.Count > 0) { node tmplocation = locations[rnd.Next(0, locations.Count - 1)]; for (int i = 0; i < n; i++) { if (sortedskus.Count > 0) { sku tmpsku = sortedskus[rnd.Next(0, sortedskus.Count - 1)]; tmpsku.location = tmplocation; sortedskus.Remove(tmpsku); } } locations.Remove(tmplocation); } } return(0); }
/// <summary> /// Sets the start node of the edge /// </summary> /// <param name="p_node">New start node</param> public void setStart(node p_node) { start = p_node; }
//If the edge is a region edge then you can add picking aisle start and end points to this edge //If the edge is a picking aisle edge then you can add pick locations to this edge /// <summary> /// If the edge is a region edge then you can add picking aisle start and end points to this edge /// If the edge is a picking aisle edge then you can add pick locations to this edge /// </summary> /// <param name="p_node">Node to be added</param> public void addOnEdgeNode(node p_node) { onedgenodes.Add(p_node); }
/// <summary> /// Fulfills pick aisles with pick locations and storage locations, returns false if no storage location can be created (because aisle is too small) /// </summary> /// <param name="p_edge">Picking aisle</param> /// <param name="p_referencenode">Reference node 1 is used for aligning storage locations correctly between picking aisles</param> /// <param name="p_referencenode2">Reference node 2 is used for aligning storage locations correctly between picking aisles</param> /// <returns>Returns false if no storage locations are created in a pick aisle, otherwise returns true</returns> public bool fulfillLocations(edge p_edge, node p_referencenode1, node p_referencenode2) { double X; double Y; double Xl1, Xr1; double Xl2, Xr2; double Xl3, Xr3; double Xl4, Xr4; double Yl1, Yr1; double Yl2, Yr2; double Yl3, Yr3; double Yl4, Yr4; double Xs, Xe, Xr, Ys, Ye, Yr; if (p_edge.getStart().getY() > p_edge.getEnd().getY()) { Xs = p_edge.getEnd().getX(); Xe = p_edge.getStart().getX(); Ys = p_edge.getEnd().getY(); Ye = p_edge.getStart().getY(); } else { Xs = p_edge.getStart().getX(); Xe = p_edge.getEnd().getX(); Ys = p_edge.getStart().getY(); Ye = p_edge.getEnd().getY(); } //Check which reference point is close to starting coordinate (Xs, Ys) if (p_referencenode1.getY() < p_referencenode2.getY()) { Xr = p_referencenode1.getX(); Yr = p_referencenode1.getY(); } else { Xr = p_referencenode2.getX(); Yr = p_referencenode2.getY(); } double angle = p_edge.calculateAngle(); double angle1 = (visualmath.radianToDegree(angle) - 90); angle = visualmath.degreeToRadian(angle1); double halfpickingaislewidth = pickingaislewidth / 2; double lengthofaisle = Math.Sqrt(Math.Pow(Xs - Xe, 2) + Math.Pow(Ys - Ye, 2)); int numberoflocations = Convert.ToInt32(Math.Ceiling(lengthofaisle / locationwidth)); double incx = (Xe - Xs) / (lengthofaisle / locationwidth); double incy = (Ye - Ys) / (lengthofaisle / locationwidth); //Find the starting point that is inside the picking aisle X = Xr; Y = Yr; if (Math.Abs(incy) > options.getEpsilon())//if the lines are not parallel then use this one { if (Yr < Ys) { if (incy > 0) { while (Y < Ys) { X = X + incx; Y = Y + incy; } } else { while (Y < Ys) { X = X - incx; Y = Y - incy; } } } else { if (incy > 0) { while (Y > Ys) { X = X - incx; Y = Y - incy; } } else { while (Y > Ys) { X = X + incx; Y = Y + incy; } } } } else//angle is zero so no y increment { if (Xr < Xs) { if (incx > 0) { while (X < Xs) { X = X + incx; Y = Y + incy; } } else { while (X < Xs) { X = X - incx; Y = Y - incy; } } } else { if (incx > 0) { while (X > Xs) { X = X - incx; Y = Y - incy; } } else { while (X > Xs) { X = X + incx; Y = Y + incy; } } } } X = X + incx * verticaladjuster; Y = Y + incy * verticaladjuster; for (int i = 0; i < numberoflocations; i++) { storagelocation s1 = null; storagelocation s2 = null; node c = new node(X, Y, options.locationnode); //Check each corner of the storage location is inside the region in a smart way //If all the corners are inside then create a storage location for left face Xl1 = X - incx / 2 - (halfpickingaislewidth + locationdepth) * Math.Cos(angle); Yl1 = Y - incy / 2 - (halfpickingaislewidth + locationdepth) * Math.Sin(angle); if (!isOnCrossAisle(Xl1, Yl1) && isInsideRegion(Xl1, Yl1)) { Xl3 = X + incx / 2 - (halfpickingaislewidth + locationdepth) * Math.Cos(angle); Yl3 = Y + incy / 2 - (halfpickingaislewidth + locationdepth) * Math.Sin(angle); if (!isOnCrossAisle(Xl3, Yl3) && isInsideRegion(Xl3, Yl3)) { Xl2 = X - incx / 2 - (halfpickingaislewidth) * Math.Cos(angle); Yl2 = Y - incy / 2 - (halfpickingaislewidth) * Math.Sin(angle); if (!isOnCrossAisle(Xl2, Yl2) && isInsideRegion(Xl2, Yl2)) { Xl4 = X + incx / 2 - (halfpickingaislewidth) * Math.Cos(angle); Yl4 = Y + incy / 2 - (halfpickingaislewidth) * Math.Sin(angle); if (!isOnCrossAisle(Xl4, Yl4) && isInsideRegion(Xl4, Yl4)) { s1 = new storagelocation(c, Xl1, Yl1, Xl2, Yl2, Xl3, Yl3, Xl4, Yl4, 1); } } } } Xr2 = X - incx / 2 + (halfpickingaislewidth + locationdepth) * Math.Cos(angle); Yr2 = Y - incy / 2 + (halfpickingaislewidth + locationdepth) * Math.Sin(angle); if (!isOnCrossAisle(Xr2, Yr2) && isInsideRegion(Xr2, Yr2)) { Xr4 = X + incx / 2 + (halfpickingaislewidth + locationdepth) * Math.Cos(angle); Yr4 = Y + incy / 2 + (halfpickingaislewidth + locationdepth) * Math.Sin(angle); if (!isOnCrossAisle(Xr4, Yr4) && isInsideRegion(Xr4, Yr4)) { Xr1 = X - incx / 2 + (halfpickingaislewidth) * Math.Cos(angle); Yr1 = Y - incy / 2 + (halfpickingaislewidth) * Math.Sin(angle); if (!isOnCrossAisle(Xr1, Yr1) && isInsideRegion(Xr1, Yr1)) { Xr3 = X + incx / 2 + (halfpickingaislewidth) * Math.Cos(angle); Yr3 = Y + incy / 2 + (halfpickingaislewidth) * Math.Sin(angle); if (!isOnCrossAisle(Xr3, Yr3) && isInsideRegion(Xr3, Yr3)) { s2 = new storagelocation(c, Xr1, Yr1, Xr2, Yr2, Xr3, Yr3, Xr4, Yr4, 1); } } } } if (s1 != null) { c.s1 = s1; } if (s2 != null) { c.s2 = s2; } //Do not create a pick location if there is no storage location if (s1 != null || s2 != null) { c.setPickingAisleEdge(p_edge); p_edge.addOnEdgeNode(c); } X = X + incx; Y = Y + incy; } //Return false if there are no pick locations created (because aisle is too short), else return true if (p_edge.getOnEdgeNodes().Count == 0) { return(false); } else { return(true); } }
/// <summary> /// Fill region with picking aisles, this method currently only works with one interior node (because of picking aisles that are split into two by another region) /// </summary> public void fill() { findRegionNodes(); //base coordinate used for rotations in regionfill node basecoordinate = new node(0, 0, options.tempnode); //temporary region edges List <edge> tempregionedges = new List <edge>(); //rotate all edges of the region by given angle for (int i = 0; i < regionedges.Count; i++) { edge tempedge = new edge(); copyedge(regionedges[i], tempedge); tempregionedges.Add(tempedge); tempregionedges[i].setStart(visualmath.rotate(basecoordinate, tempregionedges[i].getStart(), angle)); tempregionedges[i].setEnd(visualmath.rotate(basecoordinate, tempregionedges[i].getEnd(), angle)); } //Used for rectangular creation double minx = options.getBig(); double miny = options.getBig(); double maxx = options.getSmall(); double maxy = options.getSmall(); //Find the minx, miny, maxx and maxy for a box that is extreme boundaries of the region for (int i = 0; i < regionedges.Count; i++) { if (tempregionedges[i].getStart().getX() > maxx) { maxx = tempregionedges[i].getStart().getX(); } if (tempregionedges[i].getStart().getY() > maxy) { maxy = tempregionedges[i].getStart().getY(); } if (tempregionedges[i].getEnd().getX() > maxx) { maxx = tempregionedges[i].getEnd().getX(); } if (tempregionedges[i].getEnd().getY() > maxy) { maxy = tempregionedges[i].getEnd().getY(); } if (tempregionedges[i].getStart().getX() < minx) { minx = tempregionedges[i].getStart().getX(); } if (tempregionedges[i].getStart().getY() < miny) { miny = tempregionedges[i].getStart().getY(); } if (tempregionedges[i].getEnd().getX() < minx) { minx = tempregionedges[i].getEnd().getX(); } if (tempregionedges[i].getEnd().getY() < miny) { miny = tempregionedges[i].getEnd().getY(); } } //Temporary edges used to add picking aisles to the region //infeasible ones are eliminated and not added as picking aisles List <edge> tempedges = new List <edge>(); //calculate the gap between picking aisles double gap = (pickingaislewidth + 2 * locationdepth); int k = 0; //Add edges starting from minimum y point and increasing by gap up to maximum y point of the rotated region while (miny + k * gap + horizontaladjuster * gap <= maxy) { edge e1 = new edge(new node(minx, miny + k * gap + horizontaladjuster * gap, options.tempnode), new node(maxx, miny + k * gap + horizontaladjuster * gap, options.tempnode), options.tempedge); tempedges.Add(e1); k++; } //Rotate temp edges back for (int i = 0; i < tempedges.Count; i++) { tempedges[i].setStart(visualmath.rotate(basecoordinate, tempedges[i].getStart(), -angle)); tempedges[i].setEnd(visualmath.rotate(basecoordinate, tempedges[i].getEnd(), -angle)); } //Find edges that are intersecting two of the edges and add them as picking aisles of that region for (int i = 0; i < tempedges.Count; i++) { List <node> tmppoints = new List <node>(); List <int> intersectedges = new List <int>(); for (int j = 0; j < regionedges.Count; j++) { node p = new node(0, 0, options.pickingaislenode); p = regionedges[j].calculateIntersect(tempedges[i]); //Check if that edge intersect with tempedge if (p.getX() != -1000000)//-1000000 is a magic number for no intersection { tmppoints.Add(p); tmppoints[tmppoints.Count - 1].setCrossAisleEdge(regionedges[j]); } } //if there are exactly two intersections then add these two points as picking aisles to that region if (tmppoints.Count == 2) { if (angle == 90) { tmppoints.Sort((x, y) => y.getY().CompareTo(x.getY())); } else { tmppoints.Sort((x, y) => y.getX().CompareTo(x.getX())); } tmppoints[0].type = options.pickingaislenode; tmppoints[1].type = options.pickingaislenode; //If there are storage locations on this picking aisle then add this picking aisle to the region if (addPickingAisleEdge(tmppoints[0], tmppoints[1], tempedges[i].getStart(), tempedges[i].getEnd())) { tmppoints[0].getCrossAisleEdge().addOnEdgeNode(tmppoints[0]); tmppoints[1].getCrossAisleEdge().addOnEdgeNode(tmppoints[1]); } } //if there are exactly four intersections then add first two points as first picking aisle and second two points as second picking aisle if (tmppoints.Count == 4) { if (angle == 90) { tmppoints.Sort((x, y) => y.getY().CompareTo(x.getY())); } else { tmppoints.Sort((x, y) => y.getX().CompareTo(x.getX())); } //First picking aisle tmppoints[0].type = options.pickingaislenode; tmppoints[1].type = options.pickingaislenode; //If there are storage locations on this picking aisle then add this picking aisle to the region if (addPickingAisleEdge(tmppoints[0], tmppoints[1], tempedges[i].getStart(), tempedges[i].getEnd())) { tmppoints[0].getCrossAisleEdge().addOnEdgeNode(tmppoints[0]); tmppoints[1].getCrossAisleEdge().addOnEdgeNode(tmppoints[1]); } //Second picking aisle tmppoints[2].type = options.pickingaislenode; tmppoints[3].type = options.pickingaislenode; //If there are storage locations on this picking aisle then add this picking aisle to the region if (addPickingAisleEdge(tmppoints[2], tmppoints[3], tempedges[i].getStart(), tempedges[i].getEnd())) { tmppoints[2].getCrossAisleEdge().addOnEdgeNode(tmppoints[2]); tmppoints[3].getCrossAisleEdge().addOnEdgeNode(tmppoints[3]); } } } }
/// <summary> /// Add picking aisle to the region /// </summary> /// <param name="p_start">Beginning of pick aisle</param> /// <param name="p_end">Ending of pick aisle</param> /// <param name="p_referencenode1">First reference node</param> /// <param name="p_referencenode2">Second reference node</param> /// <returns>Returns true if at least one picking aisle is created, otherwise returns false</returns> public bool addPickingAisleEdge(node p_start, node p_end, node p_referencenode1, node p_referencenode2) { //Connect two picking aisle nodes, same picking aisle edge tempedge = p_start.connect(p_end, options.pickingaisleedge); //Set width of the picking aisle tempedge.setWidth(pickingaislewidth); //Set region of the picking aisle tempedge.setRegion(this); //if there is at least one storage location then create a picking aisle if (fulfillLocations(tempedge, p_referencenode1, p_referencenode2)) { //Add this edge to region picking aisle edges pickingaisleedges.Add(tempedge); return(true); } return(false); }