<template lang="pug">
.diagnostics-charts
Spinner.mt-2(v-if="isLoading")
.diagnostics-charts-first(v-if="!isLoading")
DiagnosticsChart(v-for="chartDataItem in filteredChartDataFirst()" :key="chartDataItem.name" :name="chartDataItem.name" :label="chartDataItem.label" :type="chartDataItem.type"
:color="chartDataItem.computedColor || chartDataItem.color" :dataItems="chartDataItem.data"
:options="chartDataItem.options"
@onSelection="$emit('onSelection')"
)
.diagnostics-charts-wrapper(v-if="!isLoading")
DiagnosticsChart(v-for="chartDataItem in filteredChartDataOthers()" :key="chartDataItem.name" :name="chartDataItem.name" :label="chartDataItem.label" :type="chartDataItem.type"
:color="chartDataItem.computedColor || chartDataItem.color" :dataItems="chartDataItem.data"
:options="chartDataItem.options"
@onSelection="$emit('onSelection')"
)
</template>
<script>
import { mapGetters } from 'vuex'
import DiagnosticsChart from '@c/diagnostic/DiagnosticsChart.vue'
import { datetimeToTimestamp } from '@/utils/dates.js'
import moment from 'moment'
import envService from '@/services/env-service.js'
import i18n from '@/i18n'
import { getQueryStringValue } from '@/utils/querystring'
import Spinner from '@c/shared/Spinner.vue'
import {
diagnosticsChartReferenceData,
defaultSensorsVisibility,
} from '@/services/diagnostics-service.js'
import { createEchartDateValueTooltipFormatter } from '@/utils/echarts.js'
import colors from '@/styles/colors.js'
import { queueOperationOnce } from '@/utils/promise.js'
const colorRed = colors.color_contact_off
const colorGreen = colors.color_contact_on
const immediateDeepWatchHandler = (handler) => {
return {
handler() {
return handler(this)
},
deep: true,
immediate: true,
}
}
/**
* Otherwise, multiple watchers compete for the same action
*/
function immediateDeepWatchHandlerForChartUpdate() {
return immediateDeepWatchHandler((vm) => {
queueOperationOnce(
'diagnosticscharts_immediateDeepWatchHandlerForChartUpdate',
() => {
//wait required vars (vehicleId/date) to re-compute if needed
vm.$nextTick(() => {
vm.updateChartData()
})
},
500,
{
clearPreviousTimeout: true,
}
)
})
}
function randomNumber(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
const speChartConfigurationExtraOptions = {
chartType: 'plot',
order: 5,
labelAffix: (options, vm) => (vm.isTest ? '(SPE)' : ''),
}
const speChartConfiguration = [
['speHarshBraking', 'speHarshBraking', speChartConfigurationExtraOptions],
[
'speHarshAcceleration',
'speHarshAcceleration',
speChartConfigurationExtraOptions,
],
['speHarshCornering', 'speHarshCornering', speChartConfigurationExtraOptions],
['speExcessiveSpeed', 'speExcessiveSpeed', speChartConfigurationExtraOptions],
]
const canChartConfigurationExtraOptions = {
chartType: 'line',
order: 4,
labelAffix: (options, vm) => (vm.isTest ? '(CAN)' : ''),
}
const canFuelLevelLitersClientParamName = envService.getEnvValue(
'VUE_APP_DIAGNOSTICS_CAN_FUEL_LEVEL_LITERS_CLIENT_PARAM_NAME',
'canFuelLevelLiters'
)
const canServiceDistanceClientParamName = envService.getEnvValue(
'VUE_APP_DIAGNOSTICS_CAN_SERVICE_DISTANCE_CLIENT_PARAM_NAME',
'canServiceDistanceMeters'
)
const canChartConfiguration = [
[
'canRPM',
'sensorCanRPM',
{
...canChartConfigurationExtraOptions,
show: true,
tooltip: {
formatter: createEchartDateValueTooltipFormatter(
'diagnostics.chart.units.vrm',
{
i18n,
}
),
},
},
],
[
'sensorCanConsumptionLiters',
'computedConsumptionPer100Km',
(pos, vm) => {
let itemColor = colorGreen
let refConso =
vm.$store.state.diagnostics.vehicleConfiguration
.vehicleGlobalConsumptionRef
if (refConso && pos.computedConsumptionPer100Km > refConso) {
itemColor = colorRed
}
return {
...canChartConfigurationExtraOptions,
isFakeEnabled: false,
tooltip: {
formatter: createEchartDateValueTooltipFormatter(
'diagnostics.chart.units.conso100km',
{ i18n }
),
},
item: {
itemStyle: {
color: itemColor,
},
},
/*
Echart docs says color prop support a callback function but it doesn't seem to work: https://echarts.apache.org/en/option.html#series-lines.lineStyle
//Goal: Be able to render the line with multiple colors (green/red)
lineStyle: {
color(seriesIndex, dataIndex, data, value){
//throw new Error('lineStyle' + JSON.stringify({ data, value }))
//return itemColor
return 'blue'
},
},*/
}
},
],
[
'sensorCanFuelPerc',
'sensorCanFuelPerc',
{
...canChartConfigurationExtraOptions,
tooltip: {
formatter: createEchartDateValueTooltipFormatter('%', { i18n }),
},
},
],
[
'sensorCanFuelLiters',
'sensorCanFuelLiters',
{
...canChartConfigurationExtraOptions,
tooltip: {
formatter: createEchartDateValueTooltipFormatter(
'diagnostics.chart.units.liters',
{ i18n }
),
},
/* isEnabled: async () =>
(!envService.isProduction() &&
getQueryStringValue(canFuelLevelLitersClientParamName) === '1') ||
(await authService.getClientParameter(
canFuelLevelLitersClientParamName
)) === '1',*/
},
],
[
'vehicleDistance',
'vehicleDistance',
{
...canChartConfigurationExtraOptions,
tooltip: {
formatter: createEchartDateValueTooltipFormatter('Km', { i18n }),
},
},
],
[
'sensorCanBrakePedal',
'sensorCanBrakePedal',
{
...canChartConfigurationExtraOptions,
chartType: 'plot',
},
],
[
'sensorCanBatteryPerc',
'sensorCanBatteryPerc',
{
...canChartConfigurationExtraOptions,
tooltip: {
formatter: createEchartDateValueTooltipFormatter('%', { i18n }),
},
},
],
[
'sensorCanThrottle',
'sensorCanThrottle',
{
...canChartConfigurationExtraOptions,
tooltip: {
formatter: createEchartDateValueTooltipFormatter('%', { i18n }),
},
},
],
/*[
'distanceMeters',
'canServiceDistanceMeters',
{
...canChartConfigurationExtraOptions,
chartType: 'line',
order: 4,
fakeProbability: () => true,
fakeValue: () => randomNumber(40, 65),
isEnabled: async () =>
(!envService.isProduction() &&
getQueryStringValue(canServiceDistanceClientParamName) === '1') ||
(await authService.getClientParameter(
canServiceDistanceClientParamName
)) === '1',
},
],*/
]
function durationTorFormatter(params) {
let start = moment(params.value[0]).format('DD/MM/YYYY HH:mm:ss')
let end = moment(params.value[1]).format('DD/MM/YYYY HH:mm:ss')
return `
${i18n.t('common.from')}: ${start}
<br/>
${i18n.t('common.to')}: ${end}`
}
export default {
name: 'DiagnosticsCharts',
components: {
DiagnosticsChart,
Spinner,
},
props: {
vehicleId: {
type: [String, Number],
default: '',
},
date: {
type: [String, Date],
default: '',
},
timeFrom: {
type: String,
default: '00:00',
},
timeTo: {
type: String,
default: '23:59',
},
isTest: {
type: Boolean,
default: getQueryStringValue('test') === '1',
},
},
data() {
return {
canChartConfiguration,
isCanChartConfigurationFiltered: false,
}
},
computed: {
...mapGetters({
positions: 'diagnostics/positions',
vehicleSpeedAlert: 'diagnostics/vehicleSpeedAlert',
isCanConfiguredForSelectedVehicle:
'diagnostics/isCanConfiguredForSelectedVehicle',
}),
hasValidSpeedAlert() {
return !isNaN(this.vehicleSpeedAlert) && this.vehicleSpeedAlert !== -1
},
isLoading: {
set(value) {
this.$store.state.diagnostics.isLoading = value
},
get() {
return this.$store.state.diagnostics.isLoading
},
},
chronoDataItems() {
return this.$store.state.diagnostics.chronoData.details || []
},
chartsData: {
set(value) {
this.$store.state.diagnostics.chartsData = value
},
get() {
return this.$store.state.diagnostics.chartsData
},
},
},
watch: {
isLoading() {
if (this.isLoading) {
this.$loader.showAlternative()
} else {
this.$loader.hideAlternative()
}
},
vehicleId: immediateDeepWatchHandlerForChartUpdate(),
date: immediateDeepWatchHandlerForChartUpdate(),
chronoDataItems: {
handler() {
this.updateChartData()
},
deep: true,
},
positions() {
if (this.positions.length === 0) {
this.chartsData = []
this.$forceUpdate()
}
},
},
created() {
this.isLoading = true
},
mounted() {
this.$store.state.diagnostics.isTest = this.isTest
this.$store.state.box.isTest = this.isTest
},
destroyed() {
this.$store.state.diagnostics.isTest = false
this.$store.state.box.isTest = false
},
methods: {
sortedChartData() {
if (this.chartsData.length === 0) {
return []
}
return [...this.chartsData].sort((a, b) => {
return a.order > b.order ? 1 : -1
})
},
filteredChartData() {
return this.sortedChartData().filter((c) => c.show && c.enabled !== false)
},
filteredChartDataFirst() {
return this.filteredChartData().filter((c) => c.order === 0)
},
filteredChartDataOthers() {
return this.filteredChartData().filter((c) => c.order !== 0)
},
/**
* @todo: Some Can charts will be available only if a client parameter is present
*/
async updateCanChartConfiguration() {
if (!this.isCanChartConfigurationFiltered) {
this.canChartConfiguration = await Promise.all(
this.canChartConfiguration.map((configArray) => {
return (async () => {
let options = configArray[2]
if (options.isEnabled === undefined) {
return configArray
} else {
let isEnabled = await options.isEnabled()
if (!isEnabled) {
return false
} else {
return configArray
}
}
})()
})
)
this.canChartConfiguration = this.canChartConfiguration.filter(
(configArray) => configArray !== false
)
/*console.log('canChartConfiguration has been updated', {
originalConfig: canChartConfiguration,
newConfig: this.canChartConfiguration,
})*/
}
},
async updateChartData() {
if (this._isRetrievingData) {
return
}
//console.log('updateChartData')
this.isLoading = true
this._isRetrievingData = true
await this.updateCanChartConfiguration()
await this.$store.dispatch('diagnostics/updateStore', {
vehicleId: this.vehicleId,
date: this.date,
timeFrom: this.timeFrom,
timeTo: this.timeTo,
})
this._isRetrievingData = false
this.isLoading = false
this.updateChart()
},
/**
* Lazy create and retrieves a chart item (Wrapper for chart options)
* @param {String} name Unique chart item name (i.g tor1)
* @param {String} chartOptions.chartType (plot/line/duration)
* @param {String} chartOptions.color i.g grey
* @param {String} chartOptions.order Used by this component to sort multiple charts
*/
getChartItem(name, chartOptions = {}) {
if (!this.chartsData.some((c) => c.name == name)) {
let chartItem = {
show: chartOptions.show !== undefined ? chartOptions.show : true,
label: chartOptions.label || name,
type: chartOptions.chartType || 'plot',
data: [],
color: chartOptions.color || 'grey',
order: chartOptions.order || 1,
options: { ...chartOptions },
category: chartOptions.category || 'mandatory',
...(diagnosticsChartReferenceData[name] || {}), //Injects name, label, color, category from reference dataset
...(name.includes('tor')
? diagnosticsChartReferenceData.torItems.find(
(i) => i.name == name
) || {}
: {}),
...(name.includes('ana')
? diagnosticsChartReferenceData.anaItems.find(
(i) => i.name == name
) || {}
: {}),
name, //Do not override the name
}
//Pick the provided label (i.g Conso) and fallback to a computed label otherwise (i.g ANA 1)
chartItem.label = chartOptions.label || chartItem.label
chartItem.label = this.$translation(chartItem.label, chartItem.label)
//Compute color into an actual color (Echart doesn't support css variables)
if (chartItem.color.includes('var')) {
//console.log(getComputedStyle(document.querySelector(':root')).getPropertyValue('--color-speed'))
chartItem.computedColor = getComputedStyle(
document.querySelector(':root')
).getPropertyValue(
chartItem.color.split('var(').join('').split(')').join('')
)
}
this.chartsData.push(chartItem)
/*console.log(
'Adding chart type',
name,
this.chartsData.find((c) => c.name == name)
)*/
}
return this.chartsData.find((c) => c.name == name)
},
addChartItemDataItem(chartName, xAxis, yAxis, metadata = {}, options = {}) {
let chartItem = this.getChartItem(chartName, options)
chartItem.data.push({
name: `${chartName}_${xAxis}`,
value: [xAxis, yAxis, metadata],
...(options.item || {}),
})
},
addChronoDataItems() {
this.chronoDataItems.forEach((item) => {
/**
* {"startDate":"2022-03-04 07:12:33","endDate":"2022-03-04 07:14:00","period":"00:01:27","type":"travail","iconPath":"./lib/realtimeMap/assets/picto_chrono/chrono_3.svg","distance":0,"service":"00:01:27","driverName":"","address":"Chemin Du Gué 84830 Sérignan-Du-Comtat","lat":44.185455555556,"lng":4.8415694444444,"periodDrive":"00:00:00","periodWork":"00:01:27","periodRest":"00:00:00","periodAvailable":"00:00:00","zone":"","speed":0}
*/
this.addChartItemDataItem(
'chrono',
datetimeToTimestamp(item.startDate),
datetimeToTimestamp(item.endDate),
{
type: item.typeAlt, //Used for tooltip display
},
{
category: 'chrono',
enabled:
this.isTest ||
this.$rights.hasFeatureRight('diagnostics_chart_chrono'),
label: this.$t(`diagnostics.chart.category.chrono`),
chartType: 'duration',
order: 8,
item: {
itemStyle: {
color: item.color,
},
},
tooltip: {
formatter: (params) => {
let start = moment(params.value[0]).format(
'DD/MM/YYYY HH:mm:ss'
)
let end = moment(params.value[1]).format('DD/MM/YYYY HH:mm:ss')
return `
${this.$t(`location_module.chrono.${params.value[2].type}`)}
<br/>
${this.$t('common.from')}: ${start}
<br/>
${this.$t('common.to')}: ${end}`
},
},
}
)
})
},
/**
* Fill TOR/ANA chart datasets using a single position.
* @param {Object} pos src/services/entities/position-entity.js
* @param {Object} options.filterCode ana/tor
* @param {Object} options.chartOptions See: getChartItem options
*/
addTorAnaDataItems(pos, options = {}) {
if (this.isTest && randomNumber(0, 1000) < 900) {
return //If test, render only 10% of GPS positions as ANA/TOR
}
let xAxis = pos.timestamp
let sensorTypes = [
['sensorTor', 'tor'],
['sensorAna', 'ana'],
].filter((item) =>
options.filterCode ? item[1] == options.filterCode : true
)
sensorTypes.forEach((sensorTypeItem) => {
let sensorTypeCode = sensorTypeItem[1] //ana/tor
let sensorsArray = pos[sensorTypeItem[0]]
if (sensorsArray.length === 0) {
return
}
let isAna = sensorTypeCode === 'ana'
/*if (isAna && this.isTest) {
sensorsArray = sensorsArray.map((sensorItem) => {
sensorItem.value = Math.random() * (Math.random() > 0.5 ? -1 : 1) //TEST: Random ANA value
return sensorItem
})
}*/
if (this.isTest) {
if (isAna) {
console.debug({
display: sensorsArray.filter(
(sensorItem) => sensorItem.display === true
).length,
noDisplay: sensorsArray.filter(
(sensorItem) => sensorItem.display === false
).length,
total: sensorsArray.length,
})
}
} else {
if (!isAna) {
sensorsArray = sensorsArray.filter(
(sensorItem) => sensorItem.enabled //TOR: Only if enabled
)
}
}
sensorsArray
.filter((sensorItem) => {
if (
!isAna &&
this.$store.state.diagnostics.vehicleConfiguration.boxConfigSensorAssignments.some(
(sa) => sa.sensorNumber === sensorItem.n && sa.isDurationTOR
) &&
!sensorItem.endTimestamp
) {
return false //filter out invalid duration TORs (consecutive enable/disabled)
}
return (
sensorItem.display === true || sensorItem.display === undefined
)
})
.forEach((sensorItem) => {
let sensorItemValue = sensorItem.value
//ANA: Map for chart and check constraints
if (isAna && sensorItem.normalizedValue !== undefined) {
sensorItemValue = sensorItem.normalizedValue
}
let chartType = isAna ? 'line' : 'plot'
let formatterHandler = createEchartDateValueTooltipFormatter(
sensorItem.unit || '',
{ i18n }
)
if (
!isAna &&
this.$store.state.diagnostics.vehicleConfiguration.boxConfigSensorAssignments.some(
(sa) => sa.sensorNumber === sensorItem.n && sa.isDurationTOR
)
) {
sensorItemValue = sensorItem.endTimestamp
chartType = 'duration'
formatterHandler = durationTorFormatter
}
let item = {}
if (sensorItem.color) {
item = {
itemStyle: {
color: sensorItem.color,
},
}
}
//i.g tor1, ana5, etc
let sensorCode = `${sensorTypeCode}${sensorItem.n}`
this.addChartItemDataItem(
sensorCode,
xAxis,
sensorItemValue,
{},
{
show:
defaultSensorsVisibility[sensorTypeCode] !== undefined
? defaultSensorsVisibility[sensorTypeCode]
: false,
label:
sensorItem.name.charAt(0).toUpperCase() +
sensorItem.name.slice(1) +
(this.isTest ? ` (${sensorCode.toUpperCase()})` : ''),
chartType,
item,
color: sensorItem.color || 'grey',
...(options.chartOptions || {}),
tooltip: {
//@todo: Support i18n the ana unit (unit comes from API) (i.g temps -> time)
formatter: formatterHandler,
},
}
)
})
})
},
/**
* Used for CAN/SPE
*
* @param {Object} pos position-entity
* @param {String} dataTypes[][0] Chart name (Lazy creation)
* @param {String} dataTypes[][1] Position attribute name (i.g distance)
* @param {Boolean} dataTypes[][2].isFakeEnabled Disable fake value
* @param {Function} dataTypes[][2].fakeProbability Customize fake probability (true: 100%) (false: 0%) (randomNumber(0,100)>50: 50%)
* @param {Function} dataTypes[][2].fakeValue Customize fake value
*/
addDataItems(pos, dataTypes = []) {
let chartOptions = {}
if (this.isTest) {
pos = { ...pos } //Can't mutate freezed objects
dataTypes.forEach((dataType) => {
chartOptions = dataType[2] || {}
if (chartOptions.fakeValue === false) {
return
}
if (
chartOptions.isFakeEnabled !== false && chartOptions.fakeProbability
? chartOptions.fakeProbability()
: randomNumber(0, 1000) > 950
) {
let fakeValue = 50
if (dataType[1].toLocaleLowerCase().includes('perc')) {
fakeValue = randomNumber(30, 60)
} else {
fakeValue = randomNumber(100, 500)
}
fakeValue = chartOptions.fakeValue
? chartOptions.fakeValue()
: fakeValue
pos[dataType[1]] = fakeValue
}
})
}
dataTypes.forEach((dataType) => {
chartOptions = dataType[2] || {}
if (typeof chartOptions === 'function') {
chartOptions = chartOptions(pos, this)
}
let sensorName = dataType[0].split('_')[0]
let sensorValue = pos[dataType[1]]
//Uniquement si existe et l'attribut * est renseigné
if (sensorValue != null) {
this.addChartItemDataItem(
sensorName,
pos.timestamp,
sensorValue,
{},
{
show: chartOptions.show !== undefined ? chartOptions.show : false,
label:
this.$translation(
`diagnostics.chart.category.${sensorName}`,
sensorName.toUpperCase()
) +
(chartOptions.labelAffix
? ` ${chartOptions.labelAffix(chartOptions, this)}`
: ''),
...chartOptions,
}
)
}
})
},
/**
* Source Attribut Type de graphe Position Affichage dans le Diag Paramètre utilisateur
* SPE harshBraking Plot 5 Uniquement si l'objet spe existe et l'attribut harshBraking est renseigné Non
* SPE harshAcceleration Plot 5 Uniquement si l'objet spe existe et l'attribut harshAcceleration est
* renseigné Non
* SPE harshCornering Plot 5 Uniquement si l'objet spe existe et l'attribut harshCornering est renseigné * Non
* SPE excessiveSpeed Plot 5 Uniquement si l'objet spe existe et l'attribut excessiveSpeed est renseigné * Non
*
* @param {*} pos
*/
addSPEDataItems(pos) {
this.addDataItems(pos, speChartConfiguration)
},
/**
* Source Attribut Type de graphe Position Affichage dans le Diag Paramètre utilisateur
* CAN | RPM | Line | 4 | Uniquement si le véhicule à l'option CAN | Non
* CAN FuelLevel Line 4 Uniquement si le véhicule à l'option CAN et l'attribut FuelLevel est renseigné * * * Non
* CAN fuelLevelLiters Line Uniquement si le véhicule à l'option CAN et l'attribut fuelLevelLiters est * * renseigné Oui
* CAN vehicleDistance Line 4 Uniquement si le véhicule à l'option CAN et l'attribut vehicleDistance est * * renseigné Non
* CAN brakePedal Plot 4 Uniquement si le véhicule à l'option CAN et l'attribut brakePedal est renseigné * * Non
* CAN serviceDistance Line 4 Uniquement si le véhicule à l'option CAN et l'attribut serviceDistance est * * renseigné Oui
*
* @param {*} pos
*/
addCANDataItems(pos) {
this.addDataItems(pos, this.canChartConfiguration)
},
updateChart() {
//Clear chart data
this.chartsData.length = 0
//Skip charts if no positions (Including Chrono chart)
if (this.positions.length === 0) {
return
}
this.addChronoDataItems()
let lastPosition = null
this.positions.forEach((pos, index) => {
let xAxis = pos.timestamp
//Add normal GPS dataset
this.addChartItemDataItem(
'normalGPS',
xAxis,
1,
{
lat: pos.lat,
lng: pos.lng,
},
{
item: {
itemStyle: {
color:
lastPosition?.hasContactOn == pos?.hasContactOn
? colors.color_main
: pos.hasContactOn
? colorGreen
: colorRed,
},
},
/*toolbox: {
feature: {
brush: {
icon: {
lineX: `path://M 867.72,75.66 C 867.72,75.66 867.72,433.34 867.72,433.34 867.72,433.34 105.03,433.34 105.03,433.34 105.03,433.34 105.03,75.66 105.03,75.66 105.03,75.66 867.72,75.66 867.72,75.66 Z M 867.72,75.66 C 867.72,75.66 867.72,433.34 867.72,433.34 867.72,433.34 105.03,433.34 105.03,433.34 105.03,433.34 105.03,75.66 105.03,75.66 105.03,75.66 867.72,75.66 867.72,75.66 Z M 867.72,75.66 C 867.72,75.66 867.72,433.34 867.72,433.34 867.72,433.34 105.03,433.34 105.03,433.34 105.03,433.34 105.03,75.66 105.03,75.66 105.03,75.66 867.72,75.66 867.72,75.66 Z M 974.00,0.00 C 974.00,0.00 974.00,509.00 974.00,509.00 974.00,509.00 856.47,509.00 856.47,509.00 856.47,509.00 856.47,0.00 856.47,0.00 856.47,0.00 974.00,0.00 974.00,0.00 Z M 974.00,0.00 C 974.00,0.00 974.00,509.00 974.00,509.00 974.00,509.00 856.47,509.00 856.47,509.00 856.47,509.00 856.47,0.00 856.47,0.00 856.47,0.00 974.00,0.00 974.00,0.00 Z M 974.00,0.00 C 974.00,0.00 974.00,509.00 974.00,509.00 974.00,509.00 856.47,509.00 856.47,509.00 856.47,509.00 856.47,0.00 856.47,0.00 856.47,0.00 974.00,0.00 974.00,0.00 Z M 117.53,0.00 C 117.53,0.00 117.53,509.00 117.53,509.00 117.53,509.00 0.00,509.00 0.00,509.00 0.00,509.00 0.00,0.00 0.00,0.00 0.00,0.00 117.53,0.00 117.53,0.00 Z M 117.53,0.00 C 117.53,0.00 117.53,509.00 117.53,509.00 117.53,509.00 0.00,509.00 0.00,509.00 0.00,509.00 0.00,0.00 0.00,0.00 0.00,0.00 117.53,0.00 117.53,0.00 Z M 117.53,0.00 C 117.53,0.00 117.53,509.00 117.53,509.00 117.53,509.00 0.00,509.00 0.00,509.00 0.00,509.00 0.00,0.00 0.00,0.00 0.00,0.00 117.53,0.00 117.53,0.00 Z`,
},
title: {
lineX: this.$t(
'diagnostics.chart.brush_toggle_button_title'
),
},
type: ['lineX'],
},
},
},*/
tooltip: {
formatter: (params) => {
return `${moment(params.value[0]).format('DD/MM/YYYY HH:mm:ss')}
<br/>
${this.$t('common.lat')}: ${params.value[2].lat.toFixed(
5
)}
<br/>
${this.$t('common.lng')}: ${params.value[2].lng.toFixed(
5
)}
`
},
},
}
)
let speedAlertItemOptions = {}
if (this.hasValidSpeedAlert && pos.speed > this.vehicleSpeedAlert) {
speedAlertItemOptions = {
item: {
itemStyle: {
color: 'red',
},
tooltip: {
formatter: createEchartDateValueTooltipFormatter('Km/h', {
i18n,
html: `<em style="color:'red'" class="fa fa-exclamation-circle"></em>
<br>
(${i18n.t('diagnostics.chart.speed_alert_tooltip')}: ${
this.vehicleSpeedAlert
} Km/h)`,
}),
},
},
}
}
this.addChartItemDataItem(
'speed',
xAxis,
pos.speed,
{},
{
tooltip: {
formatter: createEchartDateValueTooltipFormatter('Km/h', {
i18n,
}),
},
...speedAlertItemOptions,
}
)
if (this.isCanConfiguredForSelectedVehicle) {
this.addCANDataItems(pos) //4
}
this.addSPEDataItems(pos) //5
this.addTorAnaDataItems(pos, {
filterCode: 'ana',
chartOptions: {
order: 6,
},
})
this.addTorAnaDataItems(pos, {
filterCode: 'tor',
chartOptions: {
order: 7,
},
})
/*this.addDataItems(pos, [
[
'distanceMeters',
'distanceMeters',
{
chartType: 'line',
fakeProbability: () => true,
fakeValue: () => randomNumber(40, 65),
},
],
])*/
lastPosition = pos
})
},
},
}
</script>
<style lang="scss" scoped>
.diagnostics-charts {
width: 100%;
}
.diagnostics-charts-wrapper {
max-height: 250px;
overflow: auto;
padding-bottom: 50px;
}
.diagnostics-charts-first {
}
</style>
Source