עומסים ומגבלות

במדריך הזה מתוארים loadDemands ו-loadLimits וההקשר ביניהם.

כפי שצוין במגבלות של חלון זמן איסוף ואספקה, ההודעה OptimizeToursRequest (REST, gRPC) מכילה מספר מאפיינים שמציינים אילוצים בבעיה עם האופטימיזציה. חלק מהמאפיינים של OptimizeToursRequest מייצגים מגבלות טעינה.

לרכבים ולמשלוחים יש תכונות פיזיות שצריך להביא בחשבון כשמתכננים מסלול.

  • כלי רכב: המאפיין loadLimits מציין את העומס המקסימלי שהרכב יכול לטפל בו. עיינו במסמכי העזרה של ההודעה Vehicle (REST, gRPC).
  • משלוחים: המאפיין loadDemands מציין את העומס שצורך משלוח מסוים. עיינו במסמכי העזרה של ההודעה Shipment (REST, gRPC).

שתי האילוצים האלה מאפשרים לכלי האופטימיזציה להקצות משלוחים לכלי רכב בצורה נכונה, באופן שהכי מתאים לקיבולת הצי ולדרישות המשלוח שלכם.

שאר המסמך הזה עוסק בהרחבה בloadLimits ובloadDemands.

עומסי ביקוש ומגבלות: סוגים

אתם מציינים כל אילוץ של ביקוש והגבלה מבחינת סוג.

אתם יכולים לספק קבוצה משלכם של סוגי טעינה, כמו הדוגמאות הבאות:

  • משקל
  • עוצמת קול
  • מדידות ליניאריות
  • שמות של פריטים או ציוד שמועברים

במדריך הזה אנחנו משתמשים בweightKg כסוג לדוגמה.

גם Shipment.loadDemands וגם Vehicle.loadLimits משתמשים בסוג map של מאגרי נתונים זמניים של פרוטוקול, עם מפתחות string שמייצגים את סוגי העומס.

הערכים של Shipment.loadDemands משתמשים בהודעה Load (REST, gRPC). הודעת Load כוללת מאפיין amount אחד שמייצג את הקיבולת הנדרשת להשלמת המשלוח בסוג שצוין.

הערכים של Vehicle.loadLimits משתמשים בהודעה LoadLimit (REST, gRPC). להודעת LoadLimit יש כמה מאפיינים, כאשר maxLoad מייצג את קיבולת העומס המקסימלית של הרכב בסוג שצוין.

loadDemands של משלוח צריך את loadLimits של הרכב שהוקצה לו רק אם לשניים יש מפתחות תואמים לסוג העומס. לדוגמה, משלוח עם loadDemands של:

"loadDemands": {
  "weightKg": {
    "amount": 50
  }
}

נדרשות 50 יחידות טעינה מסוג weightKg כדי להשלים את המשלוח. רכב עם loadLimits מתוך:

"loadLimits": {
  "weightKg": {
    "maxLoad": 100
  }
}

עשויה להשלים את המשלוח, כי הערך של maxLoad של הרכב מסוג weightKg גדול מהערך loadDemands של המשלוח בסוג weightKg, או שווה לו. לעומת זאת, רכב עם loadLimits מתוך:

"loadLimits": {
  "equipmentRackStorage": {
    "maxLoad": 10
  }
}

יש קיבולת ללא הגבלה weightKg עקב היעדר מגבלת עומסים weightKg, כך שביקוש המשקל של המשלוח לא מוגבל על הרכב.

העברת מטענים בין משלוחים וכלי רכב

כשהמשלוחים נאספים ונשלחים באמצעות כלי רכב, ה-loadDemand של המשלוח עובר בין המשלוח לרכב. אפשר לראות את הטעינות של הרכב ברשומה של ההודעה OptimizeToursResponse (REST, gRPC)routes.transitions של רכב נתון. הרצף מתבצע כך:

  1. קיבולת העומס הנדרשת מוגדרת למשלוח כ-loadDemand.
  2. איסוף המשלוח מתבצע על ידי הרכב שהוקצה לו, וסכום ה-vehicleLoads של הרכב גדל בסכום של loadDemand של המשלוח. ההעברה הזו מיוצגת על ידי visits.loadDemands חיובי בהודעת התשובה.
  3. המשלוח מתקבל על ידי הרכב, וסכום ה-vehicleLoads של הרכב יורד בסכום של loadDemand של המשלוח שנמסר. ההעברה הזו מיוצגת על ידי visits.loadDemands שלילי בהודעת התשובה.

ה-vehicleLoads של הרכב לא יכול לחרוג מה-loadLimits שצוין בכל נקודה במסלול.

דוגמה מלאה עם דרישות ומגבלות של טעינה

הצגת בקשה לדוגמה עם דרישות ומגבלות של טעינה

{
  "populatePolylines": false,
  "populateTransitionPolylines": false,
  "model": {
    "globalStartTime": "2023-01-13T16:00:00Z",
    "globalEndTime": "2023-01-14T16:00:00Z",
    "shipments": [
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.789456,
              "longitude": -122.390192
            },
            "duration": "250s"
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 100.0,
        "loadDemands": {
          "weightKg": {
            "amount": 50
          }
        }
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.789116,
              "longitude": -122.395080
            },
            "duration": "250s"
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 15.0,
        "loadDemands": {
          "weightKg": {
            "amount": 10
          }
        }
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.795242,
              "longitude": -122.399347
            },
            "duration": "250s"
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 50.0,
        "loadDemands": {
          "weightKg": {
            "amount": 80
          }
        }
      }
    ],
    "vehicles": [
      {
        "endLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "startLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "costPerHour": 40.0,
        "costPerKilometer": 10.0,
        "loadLimits": {
          "weightKg": {
            "maxLoad": 100
          }
        }
      }
    ]
  }
}
    

הבקשה לדוגמה מכילה מספר פרמטרים הקשורים לעומס:

  • ערך הביקוש של shipments[0] הוא 50 weightKg.
  • ערך הביקוש של shipments[1] הוא 10 weightKg.
  • בshipments[2] יש ביקוש טעינה של 80 weightKg.
  • מגבלת הטעינה של vehicles[0] היא 100 weightKg.

הצגת תגובה לבקשה עם דרישות ומגבלות עומס

{
  "routes": [
    {
      "vehicleStartTime": "2023-01-13T16:00:00Z",
      "vehicleEndTime": "2023-01-13T16:43:27Z",
      "visits": [
        {
          "isPickup": true,
          "startTime": "2023-01-13T16:00:00Z",
          "detour": "0s",
          "loadDemands": {
            "weightKg": {
              "amount": "50"
            }
          }
        },
        {
          "shipmentIndex": 1,
          "isPickup": true,
          "startTime": "2023-01-13T16:02:30Z",
          "detour": "150s",
          "loadDemands": {
            "weightKg": {
              "amount": "10"
            }
          }
        },
        {
          "startTime": "2023-01-13T16:08:55Z",
          "detour": "150s",
          "loadDemands": {
            "weightKg": {
              "amount": "-50"
            }
          }
        },
        {
          "shipmentIndex": 1,
          "startTime": "2023-01-13T16:16:37Z",
          "detour": "343s",
          "loadDemands": {
            "weightKg": {
              "amount": "-10"
            }
          }
        },
        {
          "shipmentIndex": 2,
          "isPickup": true,
          "startTime": "2023-01-13T16:27:07Z",
          "detour": "1627s",
          "loadDemands": {
            "weightKg": {
              "amount": "80"
            }
          }
        },
        {
          "shipmentIndex": 2,
          "startTime": "2023-01-13T16:36:26Z",
          "detour": "0s",
          "loadDemands": {
            "weightKg": {
              "amount": "-80"
            }
          }
        }
      ],
      "transitions": [
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-13T16:00:00Z",
          "vehicleLoads": {
            "weightKg": {}
          }
        },
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-13T16:02:30Z",
          "vehicleLoads": {
            "weightKg": {
              "amount": "50"
            }
          }
        },
        {
          "travelDuration": "235s",
          "travelDistanceMeters": 795,
          "waitDuration": "0s",
          "totalDuration": "235s",
          "startTime": "2023-01-13T16:05:00Z",
          "vehicleLoads": {
            "weightKg": {
              "amount": "60"
            }
          }
        },
        {
          "travelDuration": "212s",
          "travelDistanceMeters": 791,
          "waitDuration": "0s",
          "totalDuration": "212s",
          "startTime": "2023-01-13T16:13:05Z",
          "vehicleLoads": {
            "weightKg": {
              "amount": "10"
            }
          }
        },
        {
          "travelDuration": "380s",
          "travelDistanceMeters": 1190,
          "waitDuration": "0s",
          "totalDuration": "380s",
          "startTime": "2023-01-13T16:20:47Z",
          "vehicleLoads": {
            "weightKg": {}
          }
        },
        {
          "travelDuration": "409s",
          "travelDistanceMeters": 1371,
          "waitDuration": "0s",
          "totalDuration": "409s",
          "startTime": "2023-01-13T16:29:37Z",
          "vehicleLoads": {
            "weightKg": {
              "amount": "80"
            }
          }
        },
        {
          "travelDuration": "171s",
          "travelDistanceMeters": 665,
          "waitDuration": "0s",
          "totalDuration": "171s",
          "startTime": "2023-01-13T16:40:36Z",
          "vehicleLoads": {
            "weightKg": {}
          }
        }
      ],
      "metrics": {
        "performedShipmentCount": 3,
        "travelDuration": "1407s",
        "waitDuration": "0s",
        "delayDuration": "0s",
        "breakDuration": "0s",
        "visitDuration": "1200s",
        "totalDuration": "2607s",
        "travelDistanceMeters": 4812,
        "maxLoads": {
          "weightKg": {
            "amount": "80"
          }
        }
      },
      "routeCosts": {
        "model.vehicles.cost_per_kilometer": 48.12,
        "model.vehicles.cost_per_hour": 28.966666666666665
      },
      "routeTotalCost": 77.086666666666659
    }
  ],
  "metrics": {
    "aggregatedRouteMetrics": {
      "performedShipmentCount": 3,
      "travelDuration": "1407s",
      "waitDuration": "0s",
      "delayDuration": "0s",
      "breakDuration": "0s",
      "visitDuration": "1200s",
      "totalDuration": "2607s",
      "travelDistanceMeters": 4812,
      "maxLoads": {
        "weightKg": {
          "amount": "80"
        }
      }
    },
    "usedVehicleCount": 1,
    "earliestVehicleStartTime": "2023-01-13T16:00:00Z",
    "latestVehicleEndTime": "2023-01-13T16:43:27Z",
    "totalCost": 77.086666666666659,
    "costs": {
      "model.vehicles.cost_per_hour": 28.966666666666665,
      "model.vehicles.cost_per_kilometer": 48.12
    }
  }
}
    

מגבלות העומס הנוספות משפיעות על הסדר של visits:

  1. בוצע איסוף של shipment[0]
  2. בוצע איסוף של shipment[1]
  3. shipment[0] נמסר
  4. shipment[1] נמסר
  5. בוצע איסוף של shipment[2]
  6. shipment[2] נמסר

ההזמנה הזו משקפת את העובדה שהרכב לא יכול להשלים שלושה משלוחים בו-זמנית, כי הערך הכולל של loadDemands שלהם גבוה מ-loadLimits של הרכב.

כל רשומת visits כוללת את השינוי בעומס על הרכב כתוצאה מהשלמת Visit. ערכי עומסים חיוביים מייצגים טעינת משלוח, ואילו ערכים שליליים מייצגים את הסרת המשלוח.

כל רשומה של transitions כוללת את עומס הרכב הכולל במהלך Transition. לדוגמה, ל-transitions[2] יש עומס weightKg של 60, שמייצג את הטעינות המשולבות של shipment[0] ו-shipment[1].

האובייקטים של המדדים routes[0].metrics ו-metrics.aggregatedRouteMetrics כוללים את המאפיין maxLoads. הערך של סוג weightKg הוא 80, שמייצג את החלק במסלול הרכב שפתח את shipments[2] למיקום המשלוח.

מגבלות על עומס רך

כמו בחלונות הזמן שמתוארים במגבלות של חלון זמן איסוף ואספקה, מגבלות על מגבלת העומס כוללות וריאציות קשות ורך. המאפיין maxLoad בהודעה LoadLimit מבטא מגבלה קשיחה: אסור שברכב יופיע עומס גדול מהערך של maxLoad בסוג שצוין. המאפיינים softMaxLoad ו-costPerUnitAboveSoftMax מבטאים מגבלה רכה, וכל יחידה שחורגת מ-softMaxLoad תצוין בעלות של costPerUnitAboveSoftMax.

למגבלות של מגבלת טעינה רכה יש כמה שימושים, כמו למשל:

  • איזון בין כמות גדולה של כלי רכב לבין המספר המינימלי הדרוש, כאשר משתלם לעשות זאת
  • הבעת העדפה של הנהגים לגבי מספר הפריטים שהם יכולים לאסוף בנוחות ולשלוח במסלול נתון
  • טעינת רכבים מתחת לקיבולת הפיזית המקסימלית שלהם כדי להגביל את הבלאי ולצמצם את עלויות התחזוקה

מגבלות על עומסים כבדים וקלים אפשר להשתמש יחד. לדוגמה, מגבלת עומס קשיח יכולה לבטא את המשקל המקסימלי של המטען שרכב יכול לשאת בצורה בטוחה, או את מספר הפריטים המקסימלי להכניס לרכב בבת אחת, ומגבלת טעינה קלה יכולה להיות המשקל המקסימלי או מספר הפריטים שעלולים להטיל מס על היכולת של הנהג להכניס את כל הפריטים ברכב.