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

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

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

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

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

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

בהמשך המסמך נסביר בפירוט על loadLimits ו-loadDemands.

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

כל אילוץ של דרישה ועומס מוגדר לפי סוג.

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

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

במדריך הזה נעשה שימוש ב-weightKg כסוג לדוגמה.

גם Shipment.loadDemands וגם Vehicle.loadLimits משתמשים בסוג map של Protocol Buffers, עם מפתחות 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 עובר בין המשלוח לבין הרכב. אפשר לראות את העומסים ברכב ברשומה routes.transitions של ההודעה OptimizeToursResponse (REST,‏ gRPC) ברכב נתון. הרצף הוא:

  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.

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

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

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