تحميل الطلبات والحدود

يوضّح هذا الدليل 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 الشحنة بين الشحنة والمركبة. يمكنك الاطّلاع على حمولات المركبة في إدخال 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 عن قيد غير صارم، حيث تُفرض تكلفة costPerUnitAboveSoftMax على كل وحدة تتجاوز softMaxLoad.

هناك عدة استخدامات لقيود الحدّ الأقصى للتحميل غير الحاسم، مثل:

  • موازنة الشحنات على أكثر من الحد الأدنى لعدد المركبات اللازمة عندما يكون ذلك مجديًا من حيث التكلفة
  • التعبير عن تفضيل السائق لعدد السلع التي يمكنه استلامها وتسليمها بشكل مريح في مسار معيّن
  • تحميل المركبات بحمولة أقل من الحد الأقصى لطاقتها المادية للحد من التآكل و خفض تكاليف الصيانة

يمكن استخدام حدود التحميل القصوى والحد الأدنى معًا. على سبيل المثال، قد يشير الحدّ الأقصى للحمولة الثابتة إلى الحدّ الأقصى لوزن الحمولة التي يمكن للمركبة حملها بأمان أو الحدّ الأقصى لعدد السلع التي يمكن وضعها في المركبة في وقت واحد، في حين قد يشير الحدّ الأقصى للحمولة المتغير إلى الحدّ الأقصى لوزن أو عدد السلع التي تتطلّب جهدًا من السائق لوضعها في المركبة.