Source

services/diagnostics-service.js

import anime from 'animejs/lib/anime.es.js'
import i18n from '@/i18n'

/**
 * Used by Diagnostics module to determine whenever to show/hide tor/ana by default
 */
export const defaultSensorsVisibility = {
  ana: true,
  tor: true,
}

/**
 * Transitions replay current position index (positions array index)
 *
 * @returns
 */
export function createReplayController() {
  let scope = {
    currentAnimationObject: null,
    currentAnimation: null,
    currentAnimationOptions: {},
    currentAnimationPlaying: false,
    wasCreated() {
      return !!scope.currentAnimation
    },
    /**
     * @warn unused
     * POC: Transition positions smoothly
     * @param {*} latLng1
     * @param {*} latLng2
     * @param {*} options
     */
    transitionLatLng(latLng1, latLng2, options) {
      let target = {
        lat: latLng1[0],
        lng: latLng1[1],
      }
      anime({
        ...options,
        targets: target,
        lat: latLng2[0],
        lng: latLng2[1],
        update() {
          options.onUpdate(target.lat, target.lng)
        },
      })
    },
    destroyAnimation() {
      if (!this.wasCreated()) {
        return
      }
      if (this.currentAnimationPlaying) {
        scope.currentAnimation.pause()
      }
      scope.currentAnimation.remove(this.currentAnimationObject)
      scope.currentAnimation = null
      scope.currentAnimationOptions.onDestroy &&
        scope.currentAnimationOptions.onDestroy()

      scope.currentAnimationPlaying = false
      scope.currentAnimationOptions.onToggle &&
        scope.currentAnimationOptions.onToggle(scope.currentAnimationPlaying)

      console.log('replay::destroyAnimation')
    },
    /**
     * Will rewind the animation to index if created
     * @param {*} index
     */
    jumpToIndex(index) {
      console.log('replay::jumpToIndex', index)
      this.destroyAnimation()
      scope.currentAnimationObject = {
        start: index,
        index,
      }
    },
    /**
     *
     * Play or stop animation
     *
     * @param {Number} options.max
     * @param {Number} options.min
     * @param {Function} options.onAnimate
     * @param {Number} options.currentIndex (set/get)
     */
    playToggle(options = {}) {
      if (options.value === false) {
        if (scope.currentAnimationPlaying) {
          scope.currentAnimation.pause()
          scope.currentAnimationPlaying = false
          //console.log('replay::stop')
          options.onToggle && options.onToggle(scope.currentAnimationPlaying)
        }

        return
      }

      if (typeof options.currentIndex !== 'number') {
        throw new Error('options.currentIndex is not a number')
      }

      if (!scope.wasCreated()) {
        //Create

        if (!scope.currentAnimationObject) {
          scope.currentAnimationObject = {
            start: options.currentIndex,
            index: options.currentIndex,
          }
        }

        scope.currentAnimationPlaying = true
        options.onToggle && options.onToggle(scope.currentAnimationPlaying)
        window.replayAnimOptions = scope.currentAnimationOptions = options
        scope.currentAnimation = window.replayAnim = anime({
          targets: scope.currentAnimationObject,
          //index: options.max,
          //easing: 'linear',
          //duration: 10000,
          ...options.animationOptions,
          round: 1,
          update: function () {
            options.currentIndex = scope.currentAnimationObject.index
            options.onAnimate(scope.currentAnimationObject.index)
          },
          complete() {
            scope.jumpToIndex(0)
          },
        })
        //console.log('replay::create-and-play')
      } else {
        //Play or pause
        if (scope.currentAnimationPlaying) {
          scope.currentAnimation.pause()
          scope.currentAnimationPlaying = false
          //console.log('replay::stop')
        } else {
          scope.currentAnimation.play()
          scope.currentAnimationPlaying = true
          //console.log('replay::play')
        }
        options.onToggle && options.onToggle(scope.currentAnimationPlaying)
      }
    },
    prev(options = {}) {
      scope.currentAnimationOptions = options
      let index =
        options.min == options.currentIndex - 1
          ? options.max
          : options.currentIndex - 1
      options.currentIndex = index
      this.jumpToIndex(index)
      options.onAnimate(index)
    },
    next(options = {}) {
      scope.currentAnimationOptions = options
      let index =
        options.max == options.currentIndex + 1 ? 0 : options.currentIndex + 1
      options.currentIndex = index
      this.jumpToIndex(index)
      options.onAnimate(index)
    },
  }

  return scope
}

export const diagnosticsChartReferenceData = {
  normalGPS: {
    name: 'normalGPS',
    label: 'diagnostics.chart.category.normal_gps_positions',
    color: 'var(--color-gps)',
    category: 'mandatory',
    isToggleDisabled: true,
    type: 'plot',
    order: 0,
  },
  speed: {
    name: 'speed',
    label: 'diagnostics.chart.category.speed',
    color: 'var(--color-speed)',
    category: 'mandatory',
    isToggleDisabled: true,
    type: 'line',
    order: 2,
  },
  canRPM: {
    name: 'canRPM',
    label: 'diagnostics.chart.category.canRPM',
    color: 'var(--color-can-rpm)',
    category: 'can',
  },
  sensorCanFuelPerc: {
    name: 'sensorCanFuelPerc',
    label: 'diagnostics.chart.category.fuelLevelPerc',
    color: 'var(--color-can-fuel-level-perc)',
    category: 'can',
  },
  sensorCanConsumptionLiters: {
    name: 'sensorCanConsumptionLiters',
    label: 'diagnostics.chart.category.sensorCanConsumptionLiters',
    color: 'var(--color-can-sensorCanConsumptionLiters)',
    category: 'can',
  },
  sensorCanFuelLiters: {
    name: 'sensorCanFuelLiters',
    label: 'diagnostics.chart.category.fuelLevelLiters',
    color: 'var(--color-can-fuel-level-liters)',
    category: 'can',
  },
  vehicleDistance: {
    name: 'vehicleDistance',
    label: 'diagnostics.chart.category.vehicleDistance',
    color: 'var(--color-can-vehicle-distance)',
    category: 'can',
  },
  sensorCanBrakePedal: {
    name: 'sensorCanBrakePedal',
    label: 'diagnostics.chart.category.sensorCanBrakePedal',
    color: 'var(--color-can-brake-pedal)',
    category: 'can',
  },
  sensorCanBatteryPerc: {
    name: 'sensorCanBatteryPerc',
    label: 'diagnostics.chart.category.sensorCanBatteryPerc',
    color: 'var(--color-can-sensorCanBatteryPerc)',
    category: 'can',
  },
  sensorCanThrottle: {
    name: 'sensorCanThrottle',
    label: 'diagnostics.chart.category.sensorCanThrottle',
    color: 'var(--color-acceleration)',
    category: 'can',
  },
  speHarshBraking: {
    name: 'speHarshBraking',
    label: 'diagnostics.chart.category.speHarshBraking',
    color: 'var(--color-harsh-braking)',
    category: 'can_spe',
  },
  speHarshAcceleration: {
    name: 'speHarshAcceleration',
    label: 'diagnostics.chart.category.speHarshAcceleration',
    color: 'var(--color-harsh-acceleration)',
    category: 'can_spe',
  },
  speHarshCornering: {
    name: 'speHarshCornering',
    label: 'diagnostics.chart.category.speHarshCornering',
    color: 'var(--color-harsh-speHarshCornering)',
    category: 'can_spe',
  },
  speExcessiveSpeed: {
    name: 'speExcessiveSpeed',
    label: 'diagnostics.chart.category.speExcessiveSpeed',
    color: 'var(--color-harsh-speExcessiveSpeed)',
    category: 'can_spe',
  },
  torItems: generateChartReferenceDataForDynamicSensor('tor'),
  anaItems: generateChartReferenceDataForDynamicSensor('ana'),
}

/**
 * @internal
 * Will compute tor/ana reference items assigning predefined colors from style variables
 * @see colors.scss
 * @param {String} sensorName tor/ana
 */
function generateChartReferenceDataForDynamicSensor(sensorName = 'tor') {
  let arr = []
  for (let i = 1; i < 9; i++) {
    arr.push({
      name: `${sensorName.toLowerCase()}${i}`,
      label: `${sensorName.toUpperCase()} ${i}`,
      color: `var(--color-sensor-${sensorName.toLowerCase()}${i})`,
      category: sensorName.toLowerCase(),
    })
  }
  return arr
}

/**
 * Will inject startTimestamp/endTimestamp to TORs with measureType "temps" (Those attributes are then used to render a duration chart)
 * @param {*} position
 * @param {*} activeTorPositions
 * @param {*} sensorAssignments
 */
export function normalizePositionTorsForDiagnosticsModule(
  position,
  activeTorPositions,
  sensorAssignments
) {
  if (!position.sensorTor) {
    throw new Error('No sensorTor property found')
  }
  if (!activeTorPositions) {
    throw new Error('activeTorPositions required')
  }
  if (!sensorAssignments) {
    throw new Error('sensorAssignments required')
  }

  position.sensorTor.forEach((sensorItem) => {
    //Use name from box configuration assignment and fallback to name from positions response
    let relatedSensorAssignment = sensorAssignments.find(
      (sa) => sa.sensorNumber === sensorItem.n && sa.sensorType === 'TOR'
    )
    sensorItem.name = relatedSensorAssignment?.name || sensorItem.name

    let activedItemIndex = activeTorPositions.findIndex(
      (activeTorItem) => activeTorItem.n === sensorItem.n
    )
    let activedItem = activeTorPositions[activedItemIndex]
    if (activedItem && sensorItem.enabled === false) {
      activeTorPositions[activedItemIndex].endTimestamp = position.timestamp
      activeTorPositions.splice(activedItemIndex, 1)
    }
    if (
      !activedItem &&
      sensorItem.enabled === true &&
      sensorAssignments.some(
        (sa) =>
          sa.sensorNumber === sensorItem.n &&
          sa.isDurationTOR &&
          sa.sensorType === 'TOR'
      )
    ) {
      sensorItem.startTimestamp = position.timestamp
      activeTorPositions.push(sensorItem)
    }
  })
}