Source

services/vehicle-service.js

import api from '@/api'
import { getAPIV3Pooling } from '@/api'
import APIUrls from '@/config/simpliciti-apis.js'
import normalizeVehicleConfigItem from '@/services/entities/vehicle-config-item.js'
import { getNestedValue } from '@/utils/object'
import normalizePositionEntity from '@/services/entities/position-entity.js'

export const VehicleIcons = (function () {
  const balayeuseIconSVGPath = `<g transform="scale(0.77) translate(0,5)"><path d="M31.2,28.827h-1V26.815a.8.8,0,0,0-.8-.8H28.344V21.223L24.3,15.835H17.986v-.993a1.19,1.19,0,0,0-1.191-1.19H1.19A1.19,1.19,0,0,0,0,14.842V27.959H1.4A3.969,3.969,0,0,0,5.444,32a3.969,3.969,0,0,0,4.041-4.041h8.082a4.041,4.041,0,0,0,8.083,0h1.742v.868H26.42a.715.715,0,0,0-.715.715v.641a.443.443,0,0,0,.443.443h.007v1.012h5.4V30.625h.006A.443.443,0,0,0,32,30.183v-.558A.8.8,0,0,0,31.2,28.827ZM5.444,29.979a2.021,2.021,0,1,1,0-4.042,2.021,2.021,0,0,1,0,4.042Zm16.165,0a2.021,2.021,0,1,1,2.021-2.021A2.072,2.072,0,0,1,21.609,29.979Zm-1.347-8.756V17.855h3.368l2.694,3.368Z" transform="translate(0 -13.652)" fill="_FILL_"/></g>`
  const BOMSVGPath = `<g transform="scale(0.8) translate(-2,5)"><path  d="M16062.411,24421.223h-7.19a3.6,3.6,0,1,1-7.19,0h-1.2v-3.592h15.576v-7.191h6l3.592,4.793v5.99h-2.4a3.6,3.6,0,1,1-7.19,0Zm1.8,0a1.8,1.8,0,1,0,1.8-1.8A1.841,1.841,0,0,0,16064.213,24421.223Zm-14.38,0a1.8,1.8,0,1,0,1.8-1.8A1.84,1.84,0,0,0,16049.833,24421.223Zm14.975-5.99h5.395l-2.4-3h-3Zm-22.766,6.039a2.044,2.044,0,0,1-2.023-2.336l1.275-8.791a3.644,3.644,0,0,1,3.427-3.145h14.711a1.311,1.311,0,0,1,1.318,1.307v7.871h-15.4l-.03,5.094Z" transform="translate(-16039.493 -24406.5)" fill="_FILL_" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1"/></g>`
  const truckSVGPath = `<g transform="scale(0.77) translate(0,5)"><path d="M-395.273,29.818a2.182,2.182,0,0,1-2.182-2.182,2.182,2.182,0,0,1,2.182-2.181,2.181,2.181,0,0,1,2.182,2.181,2.182,2.182,0,0,1-2.182,2.182m2.182-13.091,2.851,3.637h-6.487V16.727m-16,13.091a2.182,2.182,0,0,1-2.182-2.182,2.181,2.181,0,0,1,2.182-2.181,2.182,2.182,0,0,1,2.182,2.181,2.182,2.182,0,0,1-2.182,2.182m20.364-15.273h-4.364V8.727h-20.364A2.9,2.9,0,0,0-420,11.636v16h2.909A4.364,4.364,0,0,0-412.727,32a4.364,4.364,0,0,0,4.364-4.364h8.727A4.364,4.364,0,0,0-395.273,32a4.364,4.364,0,0,0,4.364-4.364H-388V20.364Z" transform="translate(420 -8.727)" fill="_FILL_"/></g>`
  const heavyTruckSVGPath = `<g transform="scale(0.8) translate(-2,5)"><path fill="_FILL" d="M150.118,454.657h-3.489v-4.67h-18.6c-1.289,0-3.722,2.207-3.722,3.5l-2.37,8.9a3.95,3.95,0,0,0-.273,1.024l-.03.112.018,0a3.766,3.766,0,0,0-.042.428,3.911,3.911,0,0,0,7.638,1.213l1.107-.008a3.488,3.488,0,1,0,6.976,0h6.978a3.488,3.488,0,1,0,6.976,0h2.326v-5.839Zm-16.277,12.262a1.751,1.751,0,1,1,1.743-1.751A1.749,1.749,0,0,1,133.841,466.919Zm13.951,0a1.751,1.751,0,1,1,1.745-1.751A1.749,1.749,0,0,1,147.792,466.919Zm-1.163-7.59v-2.92h2.908l2.278,2.92Z" transform="translate(-121.607 -449.987)"/></g>`
  const vehicleIcons = {
    //Ambulance
    //import AmbulanceIcon from 'vue-material-design-icons/Ambulance.vue'
    30010: `<g transform="scale(1) translate(0,1)"><path fill="_FILL_" d="M18,18.5A1.5,1.5 0 0,0 19.5,17A1.5,1.5 0 0,0 18,15.5A1.5,1.5 0 0,0 16.5,17A1.5,1.5 0 0,0 18,18.5M19.5,9.5H17V12H21.46L19.5,9.5M6,18.5A1.5,1.5 0 0,0 7.5,17A1.5,1.5 0 0,0 6,15.5A1.5,1.5 0 0,0 4.5,17A1.5,1.5 0 0,0 6,18.5M20,8L23,12V17H21A3,3 0 0,1 18,20A3,3 0 0,1 15,17H9A3,3 0 0,1 6,20A3,3 0 0,1 3,17H1V6C1,4.89 1.89,4 3,4H17V8H20M8,6V9H5V11H8V14H10V11H13V9H10V6H8Z" /></g>`,
    //
    //Balayeuse
    30030: balayeuseIconSVGPath,
    //Balayeuse blue
    30120: balayeuseIconSVGPath,
    //Balayeuse jaune
    30110: balayeuseIconSVGPath,
    //Balayeuse rouge
    30130: balayeuseIconSVGPath,
    //Balayeuse violette
    30140: balayeuseIconSVGPath,
    //
    //BOM
    30020: BOMSVGPath,
    //BOM blue
    30150: BOMSVGPath,
    //BOM jaune
    30160: BOMSVGPath,
    //BOM rouge
    30170: BOMSVGPath,
    //BOM violette
    30180: BOMSVGPath,
    //
    //Citerne (ex Beton)
    30240: `<g transform="scale(0.77) translate(0,5)"><path d="M-140.363,14.545h-7.273v8.727h-18.909v4.364h1.454A4.286,4.286,0,0,0-160.728,32a4.286,4.286,0,0,0,4.364-4.364h8.727A4.286,4.286,0,0,0-143.272,32a4.286,4.286,0,0,0,4.363-4.364H-136V20.363l-4.363-5.818m-20.364,15.273a2.237,2.237,0,0,1-2.181-2.182,2.237,2.237,0,0,1,2.181-2.182,2.237,2.237,0,0,1,2.182,2.182,2.237,2.237,0,0,1-2.182,2.182m17.455,0a2.237,2.237,0,0,1-2.182-2.182,2.237,2.237,0,0,1,2.182-2.182,2.237,2.237,0,0,1,2.181,2.182,2.237,2.237,0,0,1-2.181,2.182m-1.455-9.455V16.727h3.637l2.909,3.636h-6.546m-9.454-8.727a5.154,5.154,0,0,1,5.091,5.091,5.154,5.154,0,0,1-5.091,5.091h-8.727A5.154,5.154,0,0,1-168,16.727a5.154,5.154,0,0,1,5.091-5.091Z" transform="translate(168 -11.636)" fill="_FILL_"/></g>`,
    //
    //Camion blue
    30060: truckSVGPath,
    //Camion vert
    30090: truckSVGPath,
    //Camion rouge
    30100: truckSVGPath,
    //
    //Deux roues (Ex deux roues 1)
    30430: `<g transform="scale(0.77) translate(0,5)"><path id="Vélo" d="M105.334,18.667a6.546,6.546,0,0,0-2.12.374L99.28,11.333H94.667v2h3.387l1.173,2.293L96,22.84l-2.36-5.573a1.031,1.031,0,0,0,.68-.934,1,1,0,0,0-.987-1H90.667a1,1,0,1,0,0,2h.813l3,7H93.227a6.667,6.667,0,1,0,0,2h3.44l3.72-8.427,1.053,2.04a6.641,6.641,0,1,0,3.894-1.28M86.667,30a4.667,4.667,0,1,1,4.547-5.667h-5.88v2h5.88A4.661,4.661,0,0,1,86.667,30m18.667,0a4.672,4.672,0,0,1-4.667-4.667,4.614,4.614,0,0,1,1.693-3.56l2.4,4.693,1.76-.96-2.386-4.667a5.1,5.1,0,0,1,1.2-.174,4.667,4.667,0,0,1,0,9.333Z" transform="translate(-80 -11.333)" fill="_FILL_"/></g>`,
    //Deux roues (Ex deux roues 2)
    30440: `<g transform="scale(0.77) translate(0,5)"><path id="Moto" d="M24.672,17.6l-6.416-6.4H11.2v3.2h5.744l3.2,3.2H7.2a7.2,7.2,0,1,0,7.072,8.432L17.664,24a3.377,3.377,0,0,0-.064.8,7.2,7.2,0,1,0,7.2-7.2M10.944,26.016a4.008,4.008,0,1,1,0-2.416H8.368V26l2.576.016M24.752,28.8a4,4,0,1,1,4-4A4,4,0,0,1,24.752,28.8Z" transform="translate(0 -11.2)" fill="_FILL_"/></g>`,
    //
    //Ampirol (Ex Granulats)
    30360: `<g transform="scale(0.77) translate(0,5)"><path id="Ampirol" d="M27.636,14.545H20.364v8.727H1.454v4.364H2.909a4.364,4.364,0,0,0,8.728,0h8.727a4.364,4.364,0,0,0,8.727,0H32V20.364l-4.364-5.818M7.273,29.819a2.182,2.182,0,1,1,2.182-2.182,2.182,2.182,0,0,1-2.182,2.182m17.455,0a2.182,2.182,0,1,1,2.181-2.182,2.182,2.182,0,0,1-2.181,2.182m-1.455-9.455V16.728h3.637l2.851,3.636H23.272m1.455-7.273H18.909v8.728h-16L.829,14.545H0V11.636H17.455l1.455-1.454h5.819Z" transform="translate(0 -10.182)" fill="_FILL_"/></g>`,
    //
    //Piéton
    //import WalkIcon from 'vue-material-design-icons/Walk.vue'
    30080: `<path fill="_FILL_" d="M14.12,10H19V8.2H15.38L13.38,4.87C13.08,4.37 12.54,4.03 11.92,4.03C11.74,4.03 11.58,4.06 11.42,4.11L6,5.8V11H7.8V7.33L9.91,6.67L6,22H7.8L10.67,13.89L13,17V22H14.8V15.59L12.31,11.05L13.04,8.18M14,3.8C15,3.8 15.8,3 15.8,2C15.8,1 15,0.2 14,0.2C13,0.2 12.2,1 12.2,2C12.2,3 13,3.8 14,3.8Z" />`,
    //
    //Poids lourd (ex PL Jaune)
    30200: heavyTruckSVGPath,
    //Poids lourd (ex PL violet)
    30230: heavyTruckSVGPath,
    //
    //Taxi
    30040: `<g transform="scale(0.77) translate(0,5)"><path id="Taxi" d="M-222.909,18.458h-2.909l-4.364-5.819h-.951v-1.9h-2v1.9h-11.6l-4.363,5.819A2.9,2.9,0,0,0-252,21.368v4.364h2.909a4.364,4.364,0,0,0,4.363,4.364,4.364,4.364,0,0,0,4.364-4.364h8.727a4.364,4.364,0,0,0,4.364,4.364,4.364,4.364,0,0,0,4.363-4.364H-220V21.368A2.9,2.9,0,0,0-222.909,18.458Zm-21.819,9.455a2.182,2.182,0,0,1-2.181-2.182,2.182,2.182,0,0,1,2.181-2.182,2.182,2.182,0,0,1,2.182,2.182A2.182,2.182,0,0,1-244.728,27.913Zm6.546-9.455h-8.2l2.749-3.636h5.454Zm2.182,0V14.822h4.728l2.749,3.636Zm8.728,9.455a2.182,2.182,0,0,1-2.182-2.182,2.182,2.182,0,0,1,2.182-2.182,2.181,2.181,0,0,1,2.181,2.182A2.182,2.182,0,0,1-227.272,27.913Z" transform="translate(252 -10.735)" fill="_FILL_"/></g>`,
    //
    //Valise
    //import WalletTravelIon from 'vue-material-design-icons/WalletTravel.vue'
    30050: `<path fill="_FILL_" d="M20,14H4V8H7V10H9V8H15V10H17V8H20M20,19H4V17H20M9,4H15V6H9M20,6H17V4C17,2.89 16.11,2 15,2H9C7.89,2 7,2.89 7,4V6H4C2.89,6 2,6.89 2,8V19C2,20.11 2.89,21 4,21H20C21.11,21 22,20.11 22,19V8C22,6.89 21.11,6 20,6Z" />`,
    //
    //Light vehicle (Vehicule leger)
    30070: `<path fill="_FILL_" d="M16,6L19,10H21C22.11,10 23,10.89 23,12V15H21A3,3 0 0,1 18,18A3,3 0 0,1 15,15H9A3,3 0 0,1 6,18A3,3 0 0,1 3,15H1V12C1,10.89 1.89,10 3,10L6,6H16M10.5,7.5H6.75L4.86,10H10.5V7.5M12,7.5V10H17.14L15.25,7.5H12M6,13.5A1.5,1.5 0 0,0 4.5,15A1.5,1.5 0 0,0 6,16.5A1.5,1.5 0 0,0 7.5,15A1.5,1.5 0 0,0 6,13.5M18,13.5A1.5,1.5 0 0,0 16.5,15A1.5,1.5 0 0,0 18,16.5A1.5,1.5 0 0,0 19.5,15A1.5,1.5 0 0,0 18,13.5Z" />`,
  }
  return {
    getRawSVGPathFromVehicleClassName(vehicleClassName, options = {}) {
      let html = vehicleIcons[vehicleClassName]
      if (options.flipHorizontally) {
        html = html.split(`<path`).join(`<path data-flip="1"`)
      }
      return html
    },
    getRawSVGFromVehicleClassName(vehicleClassName, options = {}) {
      let html = `<svg style="width:24px;height:24px" viewBox="0 0 24 24">${this.getRawSVGPathFromVehicleClassName(
        vehicleClassName
      )}</svg>`
      if (options.color) {
        html = html.split(`<path`).join(`<path fill=${options.color} `)
      }
      return html
    },
  }
})()

/**
 * Used by Location module.
 * Get vehicle svg based on vehicle type (className)
 *
 * @param {*} vehicleClassName I.g: 30070 (Vehicule leger)
 * @param {*} options options.className override svg class
 * @returns
 */
export function getVehicleIconHTML(vehicleClassName, options = {}) {
  options.className = options.className || 'marker__inner_icon'
  options.fillColor = options.fillColor || 'black'
  let svgTemplate = `<svg class="${options.className}" style="width:24px;height:24px" viewBox="0 0 24 24">_CONTENT_</svg>`
  if (options.flipHorizontally) {
    svgTemplate = svgTemplate
      .split('style="')
      .join('style="transform:translate(-50%,-50%)scale(-1,1);')
  }
  return svgTemplate
    .split('_CONTENT_')
    .join(
      VehicleIcons.getRawSVGPathFromVehicleClassName(
        vehicleClassName,
        options
      ) || VehicleIcons.getRawSVGPathFromVehicleClassName(30060, options)
    )
    .split('_FILL_')
    .join(options.fillColor)
}

/**
 *
 * @param {Number} vehicleId
 * @returns
 */
export async function getVehicleDetailsById(vehicleId) {
  /**
   * EXAMPLE FROM APIV3
   * {
  "id": 972,
  "name": "100825",
  "categoryName": "CAPL",
  "registrationPlate": "CR156ZA",
  "hourlyCost": 0,
  "speedAlert": 90,
  "breakAlert": 60,
  "speedDriftGps": 0,
  "stopSpeedThreshold": 3,
  "stopDetectionDuration": 120,
  "lastStatementKilometer": 109740,
  "lastStatementDate": "2012-06-12T10:59:00+02:00",
  "kilometerRealized": 233730,
  "lastTreatmentDate": 1337683539,
  "tachographType": "Numerique",
  "cardReader": false,
  "firstRegistration": "-0001-11-30T00:00:00+00:09",
  "activityStart": "1970-01-01T00:00:00+01:00",
  "activityEnd": "1970-01-01T23:59:59+01:00",
  "lastConsolidation": "2013-07-17T00:00:00+02:00",
  "engineDurationUseNoted": 0,
  "engineDurationUseAutomatic": 0,
  "engineDurationUseNotedDate": "1970-01-01T00:00:00+01:00",
  "tankCapacity": 0,
  "eligibleAutoUnloading": false,
  "client": "/geored/client/clients/16",
  "category": "/geored/vehicle_categories/11731"
}
  */
  let data = (await api.v3.get(APIUrls.APIV3_VEHICLE_DETAILS + '/' + vehicleId))
    .data
  return {
    id: data.id,
    name: data.name,
    matriculationNumber: data.registrationPlate,
  }
}

/***
 * Used by diagnostics module
 * Fetch specific vehicle information: sensor assignments, speed thresholds, etc.
 */
export async function fetchVehicleConfiguration(vehicleId, options = {}) {
  let r =
    (
      await api.v3.get(
        APIUrls.APIV3_SERVICE_VEHICLE_CONFIGURATION,
        {
          vehicleId,
        },
        {
          //Deprecate: API already populate sensor item!
          /*populate: {
            items: ['associatedSensor'],
            arrayHandler(data) {
              return data.length > 0
                ? data[0]?.boxConfiguration?.sensorAssignments || false
                : false
            },
            ...(options.getOptions || {}),
          },
          ...(options.getOptions || {}),*/
        }
      )
    ).data || null
  return r instanceof Array && r.length > 0
    ? normalizeVehicleConfigItem(r[0])
    : null
}

export async function getVehicleParameter(vehicleId, parameterName) {
  let res = await api.v3.get('/geored/vehicle_parameters', {
    'vehicle.id': vehicleId,
    parameter: parameterName,
  })
  res = res.data
  return res.length > 0 ? res[0] : null
}

/**
 * @todo: JLA: @Steven This function could call normalizeEntity itself
 * @param {*} positionId
 * @returns
 */
export async function getVehiclePosition(positionId) {
  return normalizePositionEntity(
    (await api.v3.get(APIUrls.APIV3_GET_POSITION + '/' + positionId)).data
  )
}

/**
 *
 * @param {*} vehicleIds
 * @param {*} startDatetime
 * @param {*} endDatetime
 */
export async function getVehiculesPositions(
  vehicleIds,
  startDatetime,
  endDatetime
) {
  const result = (await api.v3.get(APIUrls.APIV3_GET_POSITIONS, {
    vehicleIds,
    startDatetime: startDatetime,
    endDatetime: endDatetime,
  }).data) || [null]

  console.log('results', result[0])
  if (result[0]?.positions.length > 0) {
    return normalizePositionV2(result?.positions[0])
  } else {
    return null
  }
}

/**
 * @todo Add server order/sort asc once 502 issue is resolved
 * @param {*} transformer
 * @returns
 */
export async function fetchVehicleCategories(transformer = null) {
  return (
    (await getAPIV3Pooling({
      uri: `${APIUrls.APIV3_VEHICLE_CATEGORIES}?page=1&itemsPerPage=500&order[label]=asc`,
      transform: transformer,
    })) || []
  )
}

/**
 * * @todo Add server order/sort asc once 502 issue is resolved
 * @param {*} transformer
 * @returns
 */
export async function fetchVehicles(transformer = null) {
  return (
    (await getAPIV3Pooling({
      uri: `${APIUrls.APIV3_VEHICLE}?page=1&itemsPerPage=500&order[name]=asc`,
      transform: transformer,
    })) || []
  )
}

export function normalizeVehicle(v) {
  return Object.freeze({
    id: getNestedValue(v, 'id'),
    name: getNestedValue(v, 'name'),
    categoryName: getNestedValue(v, 'categoryName'),
    categoryId: parseInt(
      getNestedValue(v, '_links.category.href').match(/\d+/)[0]
    ),
  })
}

export function normalizeVehicleToV2(v, categoryList = []) {
  const categoryId = parseInt(
    getNestedValue(v, '_links.category.href').match(/\d+/)[0]
  )
  const category = categoryList.find((c) => c.id === categoryId)
  let result = {
    id: getNestedValue(v, 'id'),
    nom: getNestedValue(v, 'name'),
    immatriculation: getNestedValue(v, 'registrationPlate'),
    categorie_id: categoryId,
    categoryName: getNestedValue(v, 'categoryName'),
  }

  if (category?.parent_id) {
    const parentCategory =
      categoryList.find((c) => c.id === category.parent_id) || null
    result = {
      ...result,
      parentCategoryName: parentCategory ? parentCategory.l : '',
    }
  }

  return result
}

export function normalizeVehicleCategoryToV2(c) {
  let result = {
    id: getNestedValue(c, 'id'),
    nom: getNestedValue(c, 'label'),
    parent_id: getNestedValue(c, 'parentId'),
    privee: getNestedValue(c, 'private'),
    ...(c.client
      ? { client: parseInt(getNestedValue(c, 'client', '').match(/\d+/)[0]) }
      : null),
    ...(c.default ? { default: getNestedValue(c, 'default', '') } : null),
  }

  return result
}

export default {
  VehicleIcons,
  fetchVehicleConfiguration,
  getVehicleParameter,
  getVehiclePosition,
  getVehiculesPositions,
  fetchVehicleCategories,
  fetchVehicles,
  normalizeVehicle,
  normalizeVehicleCategoryToV2,
  normalizeVehicleToV2,
}