# Trips ## Structure of the OTM trip Within Simacan the trip is the most important entity and is the top-level entity in most of our OTM based APIs. It is used in the OTM5 planning intake, and planning/realisation/ETA push services. Though the use cases might be different, the trip always has the same general structure. The general structure is as follows: ``` trip uuid (unique ID that is used for identifying the trip & future for updates) name (the display name) externalAttributes status (only there when cancelled, actions are then empty) actors (shipper, carrier, subcontractor, driver) vehicle actions stop location start and end times actions wait/break load/unload consignment goods move from (location reference) to (location reference) actions wait/break move (first/last mile) stop ... ``` ### Trip As mentioned before, the trip is the top-level entity and ties together all information that is relevant for all visiting multiple locations and loading and unloading consignments on those locations. A trip has the following properties: - The trip has a unique UUID that is used to identify this particular trip. This UUID is thus also used whenever the trip needs updates. Since not all planning systems work with UUIDs, it is optional. When not present the `tripId` in the external attributes will be used to deterministically generate a UUID. - A trip has an optional `name` (display name), which is to easily recognize it in the Control Tower. Note though that is doesn't have to be unique but usually is *for a single day* to differentiate trips on that day. - A trip optionally has `externalAttributes`, these are also known as *customer data* and do not serve a role on the Simacan platform, but can be used for grouping trips and shared with other parties where they might be essential. When receiving trips from the Simacan platform these external attributes also contain enriched data from the platform. - A trip at *some point* needs to be coupled to a vehicle for effective monitoring. - A trip is required to have `actions`: at least two stops are necessary. The one notably exception is when `cancelling` a trip. No actions are shared for cancelled trips. - A trip is required to have `actors`, usually a shipper and a carrier, but also a driver and subcontractor are possible. Here is a basic example (that will be fleshed out): ```json { "id": "2a8af6c6-e4d5-5dfb-9532-e8b17c3ffd88", "name": "2021-32-2-ABC", "externalAttributes": { "shift": "morning shift", "tripId": "external-system-id" }, "vehicle": {}, "actors": [], "actions": [] } ``` ### Actors Actors are organisations or individuals that have a role in the logistic operation. Within Simacan they can appear on the trip (shipper, carrier, subcontractor, driver), or on the consignment (consignee, consignor). The shipper and carrier are always expected on the trip level. The driver is optional, as are the consignor and consignee on the consignment. Actors have the following properties: - An actor has a display `name`. - An actor has a unique Simacan identifier (`code`). - An actor has one or multiple `roles`. ```json "actors": [ { "entity": { "id": "9ade6709-d76a-5406-883a-85c7df10fded", "name": "Display name carrier", "externalAttributes": { "externalId": "unique_id_carrier" } }, "roles": [ "carrier" ], "associationType": "inline" }, { "entity": { "id": "9ade6709-d76a-5406-883a-85c7df10fded", "name": "Display name shipper", "externalAttributes": { "externalId": "unique_id_shipper" } }, "roles": [ "shipper" ], "associationType": "inline" }, ... ] ``` ### Vehicle/Sensor data Ultimately a trip needs to be coupled to a vehicle to be able to be executed. However, for effective monitoring Simacan actually doesn't need the vehicle, since it also suffices to provide the *device* that is placed in the vehicle and emits the location updates. This device *can* be part of a vehicle (or the tracking unit attached to the vehicle), but it also could be the phone of the driver. Within OTM both these situations are modeled using the `vehicle`, possibly in the combination with a `sensor`. Note that OTM distinguishes *towing* and *towed* 'vehicles' by modeling the latter as `transportEquipment` and not vehicles, so the `vehicle` field only contains information about the towing vehicle. A vehicle contains the following properties: - A vehicle is a physical machine with cargo space able to transport goods. - Possibly a sensor is placed within that vehicle (OTM5 supports multiple sensors in a vehicle, but within Simacan we support only one). - The vehicle can contain properties such as `fuel` (type), `licensePlate` and `vehicleType`. - The vehicle might contain a unique identifier in the external attributes. - The vehicle does **not** contain any information about trailers. This information is added in the actions of the trip. ```json "vehicle": { "entity": { "id": "3de444dd-81cf-4e69-b27b-82e92e0dbda3", "externalAttributes": { "vehicleId": "my-external-id" }, "vehicleType": "truck", "fuel": "Euro95", "licensePlate": "AA-12-BB", "sensors": [{ "associationType": "inline", "entity": { "id": "6827dac5-776e-4474-a927-0cf695b4d9d3", "externalAttributes": { "sensorId": "id-of-the-device-in-the-vehicle", "systemId": "system-of-the-fms-device" } } }] }, "associationType": "inline" } ``` ### Actions The most interesting information of the trip is present in the actions. These contain the *locations* that need to be visited, when, and what happens on those locations. Actions consists of *stops* and *moves*. Stops contain the information of visiting a location between certain time intervals and doing other actions on that locations such as loading and unloading consignments. Moves contain the information of moving between the locations of a stop. On these moves other actions can be done as well, such as having a break, or following a specific route (first/last miles). Naturally, the actions of a trip thus always start and end with a stop, with moves in between. However, depending on the use case, moves are left out since they do not contain any relevant information. #### Stop A stop indicates visiting a location within a time interval and doing actions on that location. It consists of the following properties: - A stop has a `location`, `startTime` and an `endTime`. - A stop can have multiple `load` and `unload` actions; these actions have a `consignment`. - A stop optionally has a display `name`, `remark` and `sequenceNr`. - A stop optionally has a `stopId` within an `externalAttribute`, this identifier can be used for updating this stop. If none is present the UUID of the stop will be used. - A stop can be *cancelled*. Unlike the trip the stop will simply not appear in future messages. - A stop can be marked *administrative*. This means the stop is only their for administrative purposes and is not actively monitored in the Control Tower. These stops do not have ETAs and are not actively visited. This information is provided in the `externalAttributes` of the stop by having a key-value pair `"monitoring": "administrative"`. Non-administrative stops will simply not have such an attribute. - A stop optionally has time constraints, wich in code looks like: ```json { "id": "48c484f7-54ad-4209-b251-44abef26f6b0", "value": { "startTime": "2021-03-12T10:00:00Z", "endTime": "2021-03-12T11:00:00Z", "type": "timeWindowsConstraint" } } ``` A stop (with omitted location and consignment) could look like: ```json { "entity": { "id": "cd5be9b4-eb2e-50c1-bac9-d78d159c9d8e", "lifecycle": "planned", "externalAttributes": { "stopId": "stop123" }, "location": {}, "startTime": "2021-02-24T08:00:00Z", "endTime": "2021-02-24T08:20:00Z", "actions": [{ "entity": { "id": "85a48825-0d59-328b-b571-e3283a1f7fe7", "lifecycle": "planned", "consignment": {}, "startTime": "2021-02-24T08:00:00Z", "endTime": "2021-02-24T08:15:00Z", "actionType": "unload" }, "associationType": "inline" }, { "entity": { "id": "b7a632a2-4f2b-4efb-b75c-17132436cf39", "lifecycle": "planned", "consignment": {}, "startTime": "2021-02-24T08:15:00Z", "endTime": "2021-02-24T08:20:00Z", "actionType": "load" }, "associationType": "inline" } ], "sequenceNumber": 0, "constraint": { "entity": { "id": "10877d0c-ca94-459b-84aa-0cf10de35af6", "value": { "startTime": "2021-02-24T07:30:00Z", "endTime": "2021-02-24T09:30:00Z", "type": "timeWindowConstraint" }, "associationType": "inline" }, "actionType": "stop" } } } ``` #### Move Moves model going from one location to another. Generally, this is usually not that interesting, because a list op stops implicitly model the same thing (the move is implied in between two stops). Therefore moves are only recommended whenever they contain additional information, such as having a planned break in between two stops, or whenever a very specific route must be used for a part of travelling between the two locations. Moves have the following properties: * Moves have a from and to location, which are always references (locations are inlined on the stops). * Moves have optional actions. The actions within `move`s supported within the Simacan platform are the `wait`, `break` and `move` actions. Moves on move action imply first or last miles. Whether it is the former or the latter is implied in the name of the inner move. Next to (optional) from and to locations the inner move contains a route. The route is always represented using the GeoJson `LineString`. An example move with last mile and break could look like this: ```json { "entity": { "id": "0d5fde19-a436-3a69-8d7a-16217c55c410", "lifecycle": "planned", "from": { "uuid": "ab235853-dfdb-48f2-9240-a654fb781ea2", "entityType": "location", "associationType": "reference" }, "to": { "uuid": "3c8829a8-4a82-48ca-a24b-5fe68a519d7f", "entityType": "location", "associationType": "reference" }, "actions": [ { "entity": { "id": "7e1fc454-57b2-39d0-b34f-eca8099327de", "startTime": "2021-06-14T12:40:00Z", "endTime": "2021-06-14T13:25:00Z", "actionType": "break" }, "associationType": "inline" }, { "entity": { "id": "0b949422-8b3a-3746-8896-fbeeeba06b0c", "name": "last-mile", "lifecycle": "planned", "to": { "uuid": "3c8829a8-4a82-48ca-a24b-5fe68a519d7f", "entityType": "location", "associationType": "reference" }, "route": { "entity": { "id": "1af201e4-f2d0-3181-b4a9-dfceb406a82b", "geoReferences": { "geometry": { "coordinates": [ [ 52.50052260516744, 6.130649215855866 ], [ 52.49654643979372, 6.133894958897176 ] ], "type": "LineString" }, "type": "Feature" } }, "associationType": "inline" }, "actionType": "move" }, "associationType": "inline" } ], "actionType": "move" }, "associationType": "inline" } ``` ### Location Locations are the actual physical places visited during stops where actions - such as loading and unloading consignments - are being executed. Within Simacan we deal with two types of locations: * *managed* locations, those that are reused for different trips repeatedly. Managed locations also are visible in Master Data, can have manual geofences, and first/last mile guidance. * *unmanaged* locations, those are only of interst in one planning and thus are not visible in Master Data. These locations do not have manual geofences, and no first or last mile guidance. Both managed and unmanaged locations can be shared with and from Simacan using *inline* locations. Inline locations have the following properties: * The location contains a `name` used for display purposes. * The location optionally contains a `locationId` in the `externalAttributes` that is used for identification in Master Data and repeated use after inlining. * The location optionally contains a coordinates. * The location contains address information such as the `street`, `houseNumber`, `postalCode`, `city` and `country`. * The location contains a type. The `customer` is used for one-off (unmanaged) locations and are treated as sensitive data. They also do not appear in Master Data. The types `operationalBase` and `warehouse` are used for managed locations and appear in Master Data. * The location has optional contact details. This option can be used in case there is a contact at the location but it is neither the consignee nor the consignor. For example, to reach an administration desk or a security warden. Currently, we support the fields `phone, email, name, lastName, firstName`. ```json "location": { "entity": { "id": "a1463169-8e75-4c0c-8d4f-fbeb408f53aa", "externalAttributes": { "locationId": "simacan_123" }, "name": "Simacan", "geoReference": { "lat": 52.370216, "lon": 4.895168, "type": "latLonPointGeoReference" }, "administrativeReference": { "street": "Valutaboulevard", "houseNumber": "16", "postalCode": "1234AB", "city": "Amersfoort", "type": "addressGeoReference" }, "contactDetails": [{ "value": "Simacan", "type": "name" }, { "value": "0612345678", "type": "phone" }, { "value": "sim@can.com", "type": "email" } ], "type": "customer" }, "associationType": "inline" } ``` Locations that have been shared before can be shared using an attribute restriction: ```json "location": { "restriction": { "externalAttributes": { "locationId": "simacan_123" } }, "entityType": "location", "associationType": "attributeRestriction" }, ``` ### Consignment A consignment is the administrative label put on a set of goods that are shipped together. A consigment does not physically exist and serves only to group goods together, provide some additional information like consignor and consignee and have somethign that be tracked during the lifetime of shpping the goods. The consignment is present in the trip by being the subject of the `load`, `unload` and `handOver` actions. Within the Simacan platform the consignment has the following properties: - A consignment has a `type` (free text), this is what is known in the SCT as the *goodsFlowType*. - A consignment optionally has `externalAttributes` such as `consignmentId`, `orderId`, `userId`, `trackTraceCode`. - A consignment optionally has `goods`. Though goods are the *meat* of what is being transported, Simacan specializes in *monitoring* and thus is less concerned with what is actually shipped. A good has a quantity and optionally a productType, barCode and remark. Goods items and transportEquipment are both supported for the intake. The export will always use transportEquipment (even if the original used goods items). - Consignments can contain actors for the consignor and consignee. This can be the same actor. ```json { "id": "a556da93-46f3-4df5-8269-263bb337fde4", "actors": [ { "associationType": "inline", "entity": { "name": "some consignor" }, "roles": [ "consignor" ] }, { "associationType": "inline", "entity": { "name": "some consignee" }, "roles": [ "consignee" ] } ], "externalAttributes": { "orderId": "", "userId": "", "trackTraceCode": "" }, "type": "Frozen goods", "goods": [ { "entity": { "id": "b74d1b1a-905a-42dd-91be-1fe77f443467", "remark": "Be careful not to melt it!", "barCode": "123456789", "quantity": 3, "productType": "Ice cream", "type": "items" }, "associationType": "inline" } ] } ```