تفرض OptimizeToursRequest
قيودًا على ما يلي:
- الشحنات التي تؤثّر في كيفية تنفيذ عمليات الشحن
- المركبات التي تؤثّر في طريقة احتساب مسارات المركبات
- على مستوى العالم، ما يؤثّر في المركبات والشحنات على حدّ سواء
يركّز هذا الدليل على أحد القيود الأساسية للشحن: المواعيد الزمنية.
الفترات الزمنية هي نوع من القيود التي تقدّمها في رسالة
OptimizeToursRequest
(REST وgRPC) لتحديدحدود مستندة إلى الوقت لأنشطة الشحن. يؤثر هذا النوع من القيود في
وقت تنفيذ الشحنة وطريقة تنفيذها، بالإضافة إلى تحديد المركبة المخصّصة
للشحن. مع هذه القيود، يمنح المحسِّن الأولوية
للمركبات التي يمكنها استيفاء القيود الزمنية للشحنة على أفضل وجه.
قيود الشحن: الفترات الزمنية
يمكنك تحديد وقت استلام أو تسليم الطلب في رسالة Shipment.VisitRequest
على النحو التالي:
- استخدِم السمة
timeWindows
في الرسالة (REST وgRPC). - حدِّد وقت البدء والانتهاء في رسالة
TimeWindow
(REST، gRPC).
مثال على طلب يتضمّن قيودًا على فترة زمنية
يوضّح المثال هنا ثلاث شحنات مختلفة، ولكل منها
فترة تسليم خاصة بها. للتبسيط، يضبط هذا المثال الفترات الزمنية على deliveries
فقط، ولكن يمكن أيضًا تطبيق الفترات الزمنية على عمليات الاستلام. يمكن تحديد عدّة فترات زمنية، إلا أنّ هذا المثال يستخدم فترة واحدة فقط لكل عملية إرسال VisitRequest
.
الاطّلاع على مثال لطلب يتضمّن فترات زمنية
{ "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", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "endTime": "2023-01-13T19:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 100.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "endTime": "2023-01-13T18:30:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 20.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T17:30:00Z", "endTime": "2023-01-13T18:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 50.0 } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerHour": 40.0, "costPerKilometer": 10.0 } ] } }
مثال على استجابة تتضمّن قيودًا على الفترة الزمنية
في مثال الردّ، وقت بدء تشغيل المركبة وانتهائها هو 17:35:50
و18:17:24 على التوالي. تعكس هذه الأوقات أنّ أداة التحسين تعمل على تقليل الوقت
المطلوب لتشغيل المركبة المحدّدة في الطلب على أنّه costPerHour
مع
استيفاء جميع قيود الفترة الزمنية. يؤدي استخدام 17:35:50 كوقت بدء
إلى عدم الحاجة إلى انتظار المركبة في موقع الزيارة إلى أن تبدأ
نافذة وقت الزيارة. ويظهر ذلك في الردّ كقيم waitDuration
صفر.
الاطّلاع على ردّ على مثال الطلب مع الفترات الزمنية
{ "routes": [ { "vehicleStartTime": "2023-01-13T17:35:50Z", "vehicleEndTime": "2023-01-13T18:17:24Z", "visits": [ { "isPickup": true, "startTime": "2023-01-13T17:35:50Z", "detour": "0s" }, { "shipmentIndex": 1, "isPickup": true, "startTime": "2023-01-13T17:38:20Z", "detour": "150s" }, { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-13T17:40:50Z", "detour": "300s" }, { "shipmentIndex": 2, "startTime": "2023-01-13T17:50:09Z", "detour": "0s" }, { "shipmentIndex": 1, "startTime": "2023-01-13T18:00:00Z", "detour": "796s" }, { "startTime": "2023-01-13T18:07:35Z", "detour": "1520s" } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:35:50Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:38:20Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:40:50Z" }, { "travelDuration": "409s", "travelDistanceMeters": 1371, "waitDuration": "0s", "totalDuration": "409s", "startTime": "2023-01-13T17:43:20Z" }, { "travelDuration": "341s", "travelDistanceMeters": 1312, "waitDuration": "0s", "totalDuration": "341s", "startTime": "2023-01-13T17:54:19Z" }, { "travelDuration": "205s", "travelDistanceMeters": 636, "waitDuration": "0s", "totalDuration": "205s", "startTime": "2023-01-13T18:04:10Z" }, { "travelDuration": "339s", "travelDistanceMeters": 1276, "waitDuration": "0s", "totalDuration": "339s", "startTime": "2023-01-13T18:11:45Z" } ], "metrics": { "performedShipmentCount": 3, "travelDuration": "1294s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2494s", "travelDistanceMeters": 4595 }, "routeCosts": { "model.vehicles.cost_per_hour": 27.711111111111112, "model.vehicles.cost_per_kilometer": 45.95 }, "routeTotalCost": 73.661111111111111 } ], "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 3, "travelDuration": "1294s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2494s", "travelDistanceMeters": 4595 }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-13T17:35:50Z", "latestVehicleEndTime": "2023-01-13T18:17:24Z", "totalCost": 73.661111111111111, "costs": { "model.vehicles.cost_per_hour": 27.711111111111112, "model.vehicles.cost_per_kilometer": 45.95 } } }
تم ترتيب الفترات الزمنية حسب visits
المركبة لكي يتم تسليم الشحنات التي تتضمن
أقرب الفترات الزمنية أولاً.
- تم تسليم
shipments[2]
في الساعة 5:50 مساءً. - يتم تسليم
shipments[1]
في الساعة 6:00 مساءً. - تم تسليم
shipments[0]
في الساعة 6:07 مساءً.
يحدّد مثال الطلب قيودًا صارمة على النوافذ الزمنية، ما يتطلّب اكتمال عمليات الإرسال خلال هذه النوافذ. إذا لم يكن إكمال
VisitRequests
الشحنة خلال أي من الفترات الزمنية ممكنًا أو
مجديًا من حيث التكلفة، يتخطّى المحسِّن الشحنة. إذا كانت الشحنة تحتوي على
penaltyCost
، يضيفها المحسِّن إلى التكاليف التي يتم الإبلاغ عنها استجابةً لحالة
metrics
. بخلاف ذلك، تزداد سمة skippedMandatoryShipmentCount
لرسالة
OptimizeToursResponse
(REST وgRPC).
إذا غيّرت الفترات الزمنية من خلال تغيير فترة shipment[1]
بعد عدة ساعات
لاحقًا (إلى 21:00 من 18:00)، ستكون النتائج مختلفة كما هو موضّح في المثالين التاليين.
الاطّلاع على مثال لطلب يتضمّن فترات زمنية يتعذّر تلبيتها
{ "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", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "endTime": "2023-01-13T19:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 100.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T21:00:00Z", "endTime": "2023-01-13T21:30:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 20.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T17:30:00Z", "endTime": "2023-01-13T18:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 50.0 } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerHour": 40.0, "costPerKilometer": 10.0 } ] } }
الاطّلاع على ردّ على مثال الطلب الثاني مع الفترات الزمنية التي يتم فيها تخطّي شحنة
{ "routes": [ { "vehicleStartTime": "2023-01-13T17:37:49Z", "vehicleEndTime": "2023-01-13T18:09:49Z", "visits": [ { "isPickup": true, "startTime": "2023-01-13T17:37:49Z", "detour": "0s" }, { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-13T17:40:19Z", "detour": "150s" }, { "shipmentIndex": 2, "startTime": "2023-01-13T17:49:38Z", "detour": "0s" }, { "startTime": "2023-01-13T18:00:00Z", "detour": "946s" } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:37:49Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:40:19Z" }, { "travelDuration": "409s", "travelDistanceMeters": 1371, "waitDuration": "0s", "totalDuration": "409s", "startTime": "2023-01-13T17:42:49Z" }, { "travelDuration": "372s", "travelDistanceMeters": 1348, "waitDuration": "0s", "totalDuration": "372s", "startTime": "2023-01-13T17:53:48Z" }, { "travelDuration": "339s", "travelDistanceMeters": 1276, "waitDuration": "0s", "totalDuration": "339s", "startTime": "2023-01-13T18:04:10Z" } ], "metrics": { "performedShipmentCount": 2, "travelDuration": "1120s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "800s", "totalDuration": "1920s", "travelDistanceMeters": 3995 }, "routeCosts": { "model.vehicles.cost_per_kilometer": 39.95, "model.vehicles.cost_per_hour": 21.333333333333332 }, "routeTotalCost": 61.283333333333331 } ], "skippedShipments": [ { "index": 1 } ], "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 2, "travelDuration": "1120s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "800s", "totalDuration": "1920s", "travelDistanceMeters": 3995 }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-13T17:37:49Z", "latestVehicleEndTime": "2023-01-13T18:09:49Z", "totalCost": 81.283333333333331, "costs": { "model.shipments.penalty_cost": 20, "model.vehicles.cost_per_hour": 21.333333333333332, "model.vehicles.cost_per_kilometer": 39.95 } } }
في هذا المثال، أدّت الفترة الزمنية اللاحقة إلى تخطّي shipment[1]
،
لأنّ وقت تشغيل المركبة الإضافي المطلوب لإكمال عملية
تسليم الشحنة خلال الفترة الزمنية المحدّدة لها تجاوز تكلفة عقوبة الشحنة.
تظهر تكلفة العقوبة لـ shipment[1]
في metrics.costs
، ويظهر مؤشره
في skippedShipments
.
القيود غير الصارمة على الفترة الزمنية
كما ذكرنا بشكل موجز في مَعلمات نموذج التكلفة، يمكن تطبيق الفترات الزمنية كقيود لينة. تختلف القيود غير الصارمة عن القيود الصارمة على النحو التالي:
- القيود الصارمة: لا يمكن انتهاكها، ولا يقدّم المحسِّن حلًا يخالف القيد، حتى لو كان ذلك يعني تخطّي شحنة.
- القيود غير الصارمة: قد يتم انتهاكها، ما يعني أنّ أداة التحسين قد تقدّم حلًا ينتهك قيدًا غير صارم. ومع ذلك، يطبّق المحسِّن تكلفة أيضًا على أي انتهاك. وتقدّم هذه التكلفة كأحد المواقع الإضافية في الفترة الزمنية، وتكون عادةً كتكلفة لكل ساعة لكل ساعة قبل الفترة الزمنية التي يحدث فيها النشاط أو بعدها.
يتم تخفيف الفترات الزمنية باستخدام softStartTime
أو softEndTime
بدلاً من
startTime
أو endTime
على التوالي، وضبط
costPerHourBeforeSoftStartTime
أو costPerHourAfterSoftEndTime
.
استخدِم قيود الفترات الزمنية غير الصارمة عندما يجب أن تتم عمليات الاستلام أو التسليم خلال فترة زمنية محدّدة، ولكن ليس من الضروري تمامًا أن تتم عملية الاستلام أو التسليم خلال تلك الفترة. يمكنك استخدام قيود الفترات الزمنية الصارمة وغير الصارمة معًا للتعبير عن أهداف النشاط التجاري. على سبيل المثال:
- فترة زمنية محددة: تشير إلى ساعات عمل العميل، مثل من 9 صباحًا إلى 5 مساءً.
- الفترة الزمنية المرنة: تشير إلى الإطار الزمني للتسليم أو الاستلام الذي يتطابق مع الإشعار المُرسَل إلى العميل، مثل الساعة 9 صباحًا إلى الساعة 1 بعد الظهر.
في هذا المثال، تمّ تخفيف قيود وقت بدء الشحنة التي تم تخطّيها سابقًا لأنّ مهلة الوقت بدأت متأخرة جدًا. تم أيضًا تخفيف أوقات انتهاء الفترات الزمنية لعمليات التحميل والتفريغ الأخرى.
اطّلِع على مثال لطلب يتضمّن فترات زمنية محددة بدقة وأخرى غير محددة بدقة.
{ "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", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "softEndTime": "2023-01-13T19:00:00Z", "costPerHourAfterSoftEndTime": 2.0 } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 100.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s", "timeWindows": [ { "softStartTime": "2023-01-13T21:00:00Z", "endTime": "2023-01-13T21:30:00Z", "costPerHourBeforeSoftStartTime": 2.0 } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 20.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T17:30:00Z", "softEndTime": "2023-01-13T18:00:00Z", "costPerHourAfterSoftEndTime": 2.0 } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 50.0 } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerHour": 40.0, "costPerKilometer": 10.0 } ] } }
الاطّلاع على استجابة لمثال الطلب مع فترات زمنية محددة بدقة وغير محددة بدقة
{ "routes": [ { "vehicleStartTime": "2023-01-13T17:48:35Z", "vehicleEndTime": "2023-01-13T18:24:28Z", "visits": [ { "isPickup": true, "startTime": "2023-01-13T17:48:35Z", "detour": "0s" }, { "shipmentIndex": 1, "isPickup": true, "startTime": "2023-01-13T17:51:05Z", "detour": "150s" }, { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-13T17:53:35Z", "detour": "300s" }, { "startTime": "2023-01-13T18:00:00Z", "detour": "300s" }, { "shipmentIndex": 1, "startTime": "2023-01-13T18:07:42Z", "detour": "493s" }, { "shipmentIndex": 2, "startTime": "2023-01-13T18:17:27Z", "detour": "873s" } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:48:35Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:51:05Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:53:35Z" }, { "travelDuration": "235s", "travelDistanceMeters": 795, "waitDuration": "0s", "totalDuration": "235s", "startTime": "2023-01-13T17:56:05Z" }, { "travelDuration": "212s", "travelDistanceMeters": 791, "waitDuration": "0s", "totalDuration": "212s", "startTime": "2023-01-13T18:04:10Z" }, { "travelDuration": "335s", "travelDistanceMeters": 1204, "waitDuration": "0s", "totalDuration": "335s", "startTime": "2023-01-13T18:11:52Z" }, { "travelDuration": "171s", "travelDistanceMeters": 665, "waitDuration": "0s", "totalDuration": "171s", "startTime": "2023-01-13T18:21:37Z" } ], "metrics": { "performedShipmentCount": 3, "travelDuration": "953s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2153s", "travelDistanceMeters": 3455 }, "routeCosts": { "model.shipments.deliveries.time_windows.cost_per_hour_after_soft_end_time": 0.58166666666666667, "model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time": 5.7433333333333332, "model.vehicles.cost_per_hour": 23.922222222222221, "model.vehicles.cost_per_kilometer": 34.55 }, "routeTotalCost": 64.797222222222217 } ], "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 3, "travelDuration": "953s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2153s", "travelDistanceMeters": 3455 }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-13T17:48:35Z", "latestVehicleEndTime": "2023-01-13T18:24:28Z", "totalCost": 64.797222222222217, "costs": { "model.vehicles.cost_per_kilometer": 34.55, "model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time": 5.7433333333333332, "model.shipments.deliveries.time_windows.cost_per_hour_after_soft_end_time": 0.58166666666666667, "model.vehicles.cost_per_hour": 23.922222222222221 } } }
في المثال الذي يتضمّن قيودًا صارمة على الفترة الزمنية فقط، تم تخطّي المحتوى بالكامل
shipment[1]
، ولكن عند تخفيف الفترة الزمنية لإرساله، يتم إرساله
قبل بدء الفترة الزمنية. وبالمثل، من خلال تخفيف أوقات انتهاء
الشحنات الأخرى، يمكن تسليم shipment[2]
بعد انتهاء المهلة الزمنية.
وفي الوقت نفسه، تغيّرت كلّ من التكاليف وإجمالي الشحنات:
totalCost
: انخفض من 81.283 إلى 64.797- إجمالي الشحنات المكتملة: زاد من 2 إلى 3
عثر المحسِّن على حلّ أقل تكلفة لأنّه تمّ تخفيف قيود النافذة الزمنية مقارنةً بالمثال السابق.
أخيرًا، تتضمّن السمة metrics.costs
أيضًا مفتاحًا جديدًا للإشارة إلى
التكلفة الفعلية المتكبّدة استنادًا إلى ناتج القيود وطول
الوقت الذي تم فيه تفويت فترة التسليم. والمقصود:
-
costPerHourBeforeSoftStartTime
من 2.0 والإصدارات الأحدث - الوقت بين التسليم الفعلي وبدء الفترة الزمنية: 2.83583 ساعة
النتيجة:
model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time
:
5.6716666666666669.
تتيح لك هذه المقاييس إجراء تحليل للتكلفة لمعرفة المفاضلة بين القيود الصارمة
والقيود المرنة، والتي يمكنك استخدامها لضبط القيود كي تتماشى
بشكل أفضل مع قواعد نشاطك التجاري المحدّدة. في هذه الحالة، تكون التكلفة الإجمالية
أقل من shipment[1].penalty_cost
20.0. تبيّن لأداة التحسين أنّه أكثر فعالية من حيث التكلفة تسليم الشحنة مبكرًا بدلاً من
عدم تسليمها.