Source

services/entities/position-entity.js

/**
 * @namespace Entities
 * @category Entities
 * @module PositionEntity
 * @rules No framework-related imports
 * */

import {
  formatDatetime,
  formatTimeWithSeconds,
  datetimeToTimestamp,
} from '@/utils/dates.js'
import { getFormattedAddress } from '@/utils/address'
import { getNestedValue } from '@/utils/object.js'
import { generateShortId } from '@/utils/crypto.js'

/**
 * Normalize APIV2, APIV3, and inject computed properties.
 *
 * Used by Diagnostic module
 *
 * @example
 *
 * Output:
 * {
 * vehicleRegistrationPlate:""
 * }
 *
 * @param {*} rawData
 * @param {Boolean} options.reduced Reduced version for big lists
 * @returns {Object} Normalized entity
 *
 */
function normalizeEntity(rawData, options = {}) {
  let normalizedData = {}

  //normalize apiv2/apiv3 keys
  let convertionTables = [getAPIV2ConvertionTable(), getAPIV3ConvertionTable()]

  convertionTables.forEach((table) => {
    Object.keys(table).forEach((key) => {
      if (rawData[key] !== undefined) {
        normalizedData[table[key]] = rawData[key]
      }
    })
  })

  //add addressFields if necessary
  if (rawData.mapMatch) {
    normalizedData['streetNumber'] = rawData.mapMatch.streetNumber
    normalizedData['street'] = rawData.mapMatch.street
    normalizedData['zipCode'] = rawData.mapMatch.zipCode
    normalizedData['city'] = rawData.mapMatch.city
    normalizedData['county'] = rawData.mapMatch.county
  }

  //add computed fields
  let computedFields = getComputedFields()
  Object.keys(computedFields).forEach((key) => {
    if (typeof computedFields[key] === 'function') {
      normalizedData[key] = computedFields[key](
        normalizedData,
        rawData,
        options
      )
    }
  })

  normalizedData.id = normalizedData.id || generateShortId()

  /*console.log("position::normalizeEntity", {
    rawData,
    normalizedData,
  });*/
  return Object.freeze(normalizedData)
}

/**
 * Normalizes tor/ana from APIV2/AVPI3 response
 * @param {String} sensorName i.g TOR/ANA
 * @returns Function
 */
function normalizeSensorItemMapHandler(sensorName) {
  const sensorAttributes = {
    v2: {
      name: 'nom',
      number: 'num',
      state: 'val',
      value: 'value',
    },
    v3: {
      name: 'name',
      number: 'number',
      state: 'state',
      value: 'value',
    },
  }

  function isSensorV2Item(sensor) {
    return sensor.num !== undefined
  }
  return (sensor) => {
    let attr = isSensorV2Item(sensor)
      ? sensorAttributes.v2
      : sensorAttributes.v3

    if (sensorName === 'TOR') {
      return {
        n: sensor[attr.number],
        name: sensor[attr.name]
          ? sensor[attr.name]
          : `${sensorName}${sensor[attr.number] || '?'}`,
        enabled: sensor[attr.state] === 1,
      }
    }
    if (sensorName === 'ANA') {
      return {
        n: sensor[attr.number],
        name: sensor[attr.name]
          ? sensor[attr.name]
          : `${sensorName}${sensor[attr.number] || '?'}`,
        value: sensor[attr.value],
      }
    }
    return {
      ...sensor,
    }
  }
}

function getComputedFields() {
  return {
    vehicleId: (normalizedData, rawData, options) =>
      normalizedData.vehicleId || options.vehicleId,
    datetimeFormatted: (normalizedData, rawData, options) =>
      formatDatetime(normalizedData.datetime, {
        inputFormat: options.datetimeInputFormat,
      }),
    timeFormatted: (normalizedData) =>
      formatTimeWithSeconds(normalizedData.datetime),
    addressFormatted: (normalizedData, rawData) =>
      getFormattedAddress(normalizedData),
    timestamp: (normalizedData, rawData, options) =>
      datetimeToTimestamp(normalizedData.datetime, options.datetimeInputFormat),
    speedFormatted: (normalizedData) => `${normalizedData.speed} Km/h`,

    sensorTor: (normalizedData, rawData) =>
      getNestedValue(rawData, 'tor', [])
        .filter((tor) => tor.etat !== null)
        .map(normalizeSensorItemMapHandler('TOR')),
    sensorAna: (normalizedData, rawData) =>
      getNestedValue(rawData, 'ana', [])
        .filter((ana) => ana.etat !== null)
        .map(normalizeSensorItemMapHandler('ANA')),

    sensorCanCruise: (normalizedData, rawData) =>
      getNestedValue(rawData, ['can_cruise', 'can.chronoCruiseControl'], null),
    sensorCanBrakePedal: (normalizedData, rawData) =>
      getNestedValue(rawData, ['can.brakePedal'], null),
    sensorCanRPM: (normalizedData, rawData) =>
      getNestedValue(rawData, ['can_vrm', 'can.vrm'], null),

    sensorCanFuelPerc: (normalizedData, rawData) =>
      getNestedValue(rawData, ['can_fuel'], null),
    sensorCanFuelLiters: (normalizedData, rawData) =>
      getNestedValue(rawData, ['can.fuelLevelLiters'], null),

    sensorCanConsumptionLiters: (normalizedData, rawData) =>
      getNestedValue(rawData, ['can_conso', 'can.consumption'], null),
    sensorCanThrottle: (normalizedData, rawData) =>
      getNestedValue(rawData, ['can_accelerateur', 'can.accelerator'], null),
    vehicleDistance: (normalizedData, rawData) =>
      getNestedValue(rawData, ['can.vehicleDistance'], null),

    canServiceDistanceMeters: (normalizedData, rawData) =>
      getNestedValue(rawData, ['can.serviceDistance'], null),
    sensorCanBatteryPerc: (normalizedData, rawData) =>
      getNestedValue(rawData, ['can.battery'], null),

    speHarshBraking: (normalizeEntity, rawData) =>
      getNestedValue(rawData, ['harsh_braking', 'spe.harshBraking'], null),
    speHarshAcceleration: (normalizeEntity, rawData) =>
      getNestedValue(
        rawData,
        ['harsh_acceleration', 'spe.harshAcceleration'],
        null
      ),
    speHarshCornering: (normalizeEntity, rawData) =>
      getNestedValue(rawData, ['harsh_cornering', 'spe.harshCornering'], null),
    speExcessiveSpeed: (normalizeEntity, rawData) =>
      getNestedValue(
        rawData,
        ['vitesse_excessive', 'spe.excessiveSpeed'],
        null
      ),
  }
}

function getAPIV3ConvertionTable() {
  return {
    id: 'id',
    latitude: 'lat',
    longitude: 'lng',
    datetime: 'datetime',
    vehicleId: 'vehicleId',
    speed: 'speed',
    boxNumber: 'boxNumber',
    distance: 'distanceMeters',
    hasContactOn: 'hasContactOn',
    receptionDatetime: 'receptionDatetime',
  }
}

/**
 * Transform positions from both /historique/positions and /historique/positions_capteurs
 * @returns
 */
function getAPIV2ConvertionTable() {
  return {
    code: 'code', //position_capteurs items have code (TOR1,POS,etc)
    code_capteurs: 'code_capteurs', //used by map cmp to render/filter sensors positions.
    vehicule_id: 'vehicleId',
    client_id: 'clientId',
    //categorie_vehicule_id: "vehicleCategoryId",
    //client_nom: "clientName",
    vehicule_nom: 'vehicleName',
    vehicule_immatriculation: 'vehicleRegistrationPlate',
    //categorie_vehicule_nom: "vehicleCategoryName",
    latitude: 'lat',
    longitude: 'lng',
    lon: 'lng',
    lat: 'lat',
    dateheure: 'datetime',
    flag_contact: 'hasContactOn',
    //qualitegps: "gpsQuality",
    vitesse: 'speed',
    //altitude: "altitude",
    //dateheure_reception: "receptionDatetime",
    //calculposition: "calculposition",
    //etatelec: 'etatelec',
    //flag_demarage: "flag_demarage",
    //distance_m: "distance_m",
    //troncon: "troncon",
    //flag: "flag",
    //capteurs: "capteurs",
    //tor1: 'tor1',
    //tor2: 'tor2',
    //tor3: 'tor3',
    //tor4: 'tor4',
    //tor5: 'tor5',
    //tor6: 'tor6',
    //tor7: 'tor7',
    //tor8: 'tor8',
    //ana1: 'ana1',
    //ana2: 'ana2',
    //ana3: 'ana3',
    //analog_input_1: 'analog_input_1',
    //digital_output_1: 'digital_output_1',
    can_conso: 'canConsumption',
    //can_vrm: "canVRM",
    can_accelerateur: 'canAccelerator',
    can_cruise: 'sensorCanCruise',
    //can_fuel: "canFuel",
    //longitude_mapmatch: "longitude_mapmatch",
    //latitude_mapmatch: "latitude_mapmatch",
    numero_rue: 'streetNumber',
    rue: 'streetName',
    code_route: 'code_route',
    codepostal: 'zipCode',
    ville: 'city',
    //departement: "departement",
    //region: "region",
    //pays: "pays",
  }
}

export default normalizeEntity

export function normalizePositionV2(data) {
  return {
    id: data.id,
    lat: getNestedValue(data, ['lat']),
    lon: getNestedValue(data, ['lon']),
    couleur: getNestedValue(data, ['couleur', 'color']),
    code: getNestedValue(data, ['code']),
    code_capteurs: getNestedValue(data, ['tor', 'code_capteurs']),
    dateheure80: getNestedValue(data, ['dateheure80', 't80']),
    dateheure: getNestedValue(data, ['dateheure', 'dt']),
    flag_contact: getNestedValue(data, ['flag_contact', 'contact']),
    qualite_gps: getNestedValue(data, ['qualite_gps', 'quality']),
    vitesse: getNestedValue(data, ['vitesse', 'speed']),
  }
}