214 lines
6.6 KiB
JavaScript
214 lines
6.6 KiB
JavaScript
window.gamepad.template = class TelemetryTemplate {
|
|
/**
|
|
* Instanciates a new telemetry template
|
|
*/
|
|
constructor() {
|
|
this.gamepad = window.gamepad;
|
|
this.init();
|
|
}
|
|
|
|
/**
|
|
* Destroys the template
|
|
*/
|
|
destructor() {
|
|
this.running = false;
|
|
}
|
|
|
|
/**
|
|
* Converts a value to a percentage
|
|
*
|
|
* @param {Number} value
|
|
* @param {Number} min
|
|
* @param {Number} max
|
|
* @returns {Number}
|
|
*/
|
|
toPercentage(value, min, max) {
|
|
return value !== undefined
|
|
? Math.round((value - min) * (100 / (max - min)))
|
|
: 0;
|
|
}
|
|
|
|
/**
|
|
* Converts a value to degrees
|
|
*
|
|
* @param {Number} value
|
|
* @param {Number} min
|
|
* @param {Number} max
|
|
* @returns {Number}
|
|
*/
|
|
toDegrees(value, min, max) {
|
|
const percentage = this.toPercentage(value, min, max);
|
|
return (this.directionDegrees) * (percentage - 50) / 100;
|
|
}
|
|
|
|
/**
|
|
* Set the value of an axis
|
|
*
|
|
* @param {object} gamepad
|
|
* @param {string} axis
|
|
* @returns {Number}
|
|
*/
|
|
toAxisValue(gamepad, axis) {
|
|
const { [`${axis}Type`]: type, [`${axis}Index`]: index, [`${axis}Min`]: min, [`${axis}Max`]: max } = this[axis];
|
|
const value = type === 'button' ? gamepad.buttons[index].value : gamepad.axes[index];
|
|
return axis === 'direction' ? this.toDegrees(value, min, max) : this.toPercentage(value, min, max);
|
|
}
|
|
|
|
/**
|
|
* Loads the axes
|
|
*/
|
|
loadAxes() {
|
|
this.AXES.forEach((axis) => {
|
|
this[axis] = {
|
|
[`${axis}Type`]: (gamepad.getUrlParam(`${axis}Type`) || 'axis').replace('axis', 'axe'),
|
|
[`${axis}Index`]: gamepad.getUrlParam(`${axis}Index`),
|
|
[`${axis}Min`]: gamepad.getUrlParam(`${axis}Min`) || -1,
|
|
[`${axis}Max`]: gamepad.getUrlParam(`${axis}Max`) || 1,
|
|
}
|
|
});
|
|
this.directionDegrees = gamepad.getUrlParam('directionDegrees') || 360;
|
|
}
|
|
|
|
/**
|
|
* Initializes the template
|
|
*/
|
|
init() {
|
|
this.$fps = document.querySelector('#fps');
|
|
this.$clutchBar = document.querySelector('#clutch .bar');
|
|
this.$clutchValue = document.querySelector('#clutch .value');
|
|
this.$brakeBar = document.querySelector('#brake .bar');
|
|
this.$brakeValue = document.querySelector('#brake .value');
|
|
this.$throttleBar = document.querySelector('#throttle .bar');
|
|
this.$throttleValue = document.querySelector('#throttle .value');
|
|
this.$directionIndicator = document.querySelector('#direction .indicator');
|
|
this.AXES = ['clutch', 'brake', 'throttle', 'direction'];
|
|
this.frequency = gamepad.getUrlParam('fps') || 60;
|
|
this.historyLength = gamepad.getUrlParam('history') || 5000;
|
|
this.index = 0;
|
|
this.interval = 1000 / this.frequency;
|
|
this.length = this.historyLength / this.interval;
|
|
this.loadAxes();
|
|
this.initChart();
|
|
}
|
|
|
|
/**
|
|
* Initializes the live chart
|
|
*/
|
|
initChart() {
|
|
const script = document.createElement('script');
|
|
script.async = true;
|
|
script.src = `https://www.gstatic.com/charts/loader.js`;
|
|
script.onload = () => {
|
|
if (!google || !google.visualization) {
|
|
this.loadGoogleCharts();
|
|
return;
|
|
}
|
|
this.drawChart();
|
|
};
|
|
this.gamepad.$gamepad.appendChild(script);
|
|
}
|
|
|
|
/**
|
|
* Loads the Google Charts library
|
|
*/
|
|
loadGoogleCharts() {
|
|
google.charts.load('current', { packages: ['corechart', 'line'], });
|
|
google.charts.setOnLoadCallback(this.drawChart.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Draws the live chart with the initial data
|
|
*/
|
|
drawChart() {
|
|
this.initialData = [['time', 'clutch', 'brake', 'throttle']];
|
|
for (this.index = 0; this.index < this.length; this.index++) {
|
|
this.initialData.push([this.index, 0, 0, 0]);
|
|
}
|
|
this.data = google.visualization.arrayToDataTable(this.initialData);
|
|
this.options = {
|
|
backgroundColor: 'transparent',
|
|
chartArea: {
|
|
left: 0,
|
|
top: 0,
|
|
width: '100%',
|
|
height: '100%',
|
|
backgroundColor: 'transparent',
|
|
},
|
|
hAxis: {
|
|
textPosition: 'none',
|
|
gridlines: {
|
|
color: 'transparent',
|
|
}
|
|
},
|
|
vAxis: {
|
|
textPosition: 'none',
|
|
gridlines: {
|
|
color: 'transparent',
|
|
},
|
|
minValue: 0,
|
|
maxValue: 100,
|
|
viewWindow: {
|
|
min: 2,
|
|
max: 102,
|
|
}
|
|
},
|
|
colors: ['#2D64B9', '#A52725', '#0CA818'],
|
|
legend: 'none'
|
|
};
|
|
this.chart = new google.visualization.LineChart(document.querySelector('#chart'));
|
|
this.chart.draw(this.data, this.options);
|
|
|
|
this.running = true;
|
|
this.update();
|
|
}
|
|
|
|
/**
|
|
* Updates the live chart and the meters
|
|
*/
|
|
update() {
|
|
if (!this.running) return;
|
|
|
|
const activeGamepad = this.gamepad.getActive();
|
|
if (!activeGamepad) return;
|
|
const [clutch, brake, throttle, direction] = this.AXES.map((axis) => this.toAxisValue(activeGamepad, axis));
|
|
this.updateChart(clutch, brake, throttle);
|
|
this.updateMeters(clutch, brake, throttle, direction);
|
|
|
|
window.setTimeout(this.update.bind(this), this.interval);
|
|
}
|
|
|
|
/**
|
|
* Updates the live chart with the latest data
|
|
*
|
|
* @param {Number} clutch
|
|
* @param {Number} brake
|
|
* @param {Number} throttle
|
|
*/
|
|
updateChart(clutch, brake, throttle) {
|
|
this.data.removeRows(0, 1);
|
|
this.data.addRow([this.index, clutch, brake, throttle]);
|
|
this.chart.draw(this.data, this.options);
|
|
this.index++;
|
|
}
|
|
|
|
/**
|
|
* Updates the meters with the latest data
|
|
*
|
|
* @param {Number} clutch
|
|
* @param {Number} brake
|
|
* @param {Number} throttle
|
|
* @param {Number} direction
|
|
*/
|
|
updateMeters(clutch, brake, throttle, direction) {
|
|
Object.entries({ clutch, brake, throttle, direction }).forEach(([axis, value]) => {
|
|
if (axis === 'direction') {
|
|
this.$directionIndicator.style.transform = `rotate(${value}deg)`;
|
|
return;
|
|
}
|
|
this[`$${axis}Value`].innerHTML = value;
|
|
this[`$${axis}Value`].style.opacity = `${Math.round(33 + (value / 1.5))}%`;
|
|
this[`$${axis}Bar`].style.height = `${value}%`;
|
|
});
|
|
}
|
|
};
|