/**
* @author Serge Babayan
* @module views/AttitudeView
* @requires models/Commands
* @requires util/Validator
* @requires util/Logger
* @requires models/TelemetryData
* @requires util/Template
* @requires electron
* @listens models/TelemetryData~TelemetryData:data_received
* @copyright Waterloo Aerial Robotics Group 2016
* @licence https://raw.githubusercontent.com/UWARG/WARG-Ground-Station/master/LICENSE
* @description Responsible for displaying a the aircraft's attitude via dials (heading, roll, pitch)
*/
var remote = require('electron').remote;
var Template = require('../util/Template');
var TelemetryData = remote.require('./app/models/TelemetryData');
var Logger = remote.require('./app/util/Logger');
var Validator = require('../util/Validator');
var Commands = remote.require('./app/models/Commands');
module.exports = function (Marionette) {
return Marionette.ItemView.extend({
template: Template('AttitudeView'),
className: 'attitudeView',
ui: {
attitude_dials: '.dial-picture',
pitch_dial: '.pitch-dial .dial-picture .fluid',
roll_dial: '.roll-dial .dial-picture .fluid',
heading_dial: '.heading-dial .dial-picture .image-overlay',
horizon_dial: '.horizon-dial .dial-picture .fluid',
pitch_text: '.pitch-dial .current-value',
roll_text: '.roll-dial .current-value',
heading_text: '.heading-dial .current-value',
pitch_setpoint: '.pitch-dial .auto-pilot-setpoint',
roll_setpoint: '.roll-dial .auto-pilot-setpoint',
heading_setpoint: '.heading-dial .auto-pilot-setpoint',
roll_input: '.roll-dial form input',
pitch_input: '.pitch-dial form input',
heading_input: '.heading-dial form input'
},
events: {
'submit .roll-dial form': 'sendSetRoll',
'submit .pitch-dial form': 'sendSetPitch',
'submit .heading-dial form': 'sendSetHeading'
},
initialize: function () {
this.current_pitch = null;
this.current_roll = null;
this.current_heading = null;
this.telemetry_callback = null;
},
onRender: function () {
this.ui.attitude_dials.parent().resize(this.setCanvasDimensions.bind(this));
this.telemetry_callback = this.telemetryCallback.bind(this);
TelemetryData.addListener('data_received', this.telemetry_callback);
},
onBeforeDestroy: function () {
TelemetryData.removeListener('data_received', this.telemetry_callback);
},
telemetryCallback: function (data) {
this.setPitch(data.pitch);
this.setRoll(data.roll);
this.setHeading(data.heading);
this.setPitchSetpoint(data.pitch_setpoint);
this.setRollSetpoint(data.roll_setpoint);
this.setHeadingSetpoint(data.heading_setpoint);
},
setCanvasDimensions: function () {
var canvas_dimensions = Math.min(this.ui.attitude_dials.parent().width() - 12, this.ui.attitude_dials.parent().height() - 105);
if (canvas_dimensions && canvas_dimensions > 100) {
this.ui.attitude_dials.css({
width: canvas_dimensions,
height: canvas_dimensions
});
}
},
setPitch: function (pitch) {
if (Validator.isValidPitch(pitch)) {
this.ui.pitch_text.text(Number(pitch).toFixed(1));
var int_pitch = Math.round(Number(pitch));
if (int_pitch !== this.current_pitch) { //we only update the display if its something different
this.current_pitch = int_pitch;
var pitchPercent = (Number(pitch) / 90).toFixed(2);
var dial_height = this.ui.pitch_dial.parent().height() / 2;
this.ui.horizon_dial.css('height', Math.round(dial_height + dial_height * pitchPercent) + 'px');
this.ui.pitch_dial.css('transform', 'rotate(' + int_pitch + 'deg) scale(2)');
}
} else {
this.ui.pitch_dial.css('transform', 'rotate(0deg) scale(2)');
Logger.warn('Invalid pitch value received! Pitch: ' + pitch);
}
},
setRoll: function (roll) {
if (Validator.isValidRoll(roll)) {
this.ui.roll_text.text(Number(roll).toFixed(1));
var int_roll = Math.round(Number(roll));
if (int_roll !== this.current_roll) { //we only update the display if its something different
this.current_roll = int_roll;
this.ui.horizon_dial.css('transform', 'rotate(' + -1 * int_roll + 'deg) scale(2)'); //multiply by negative 1 because thats how roll works on a horizon dial
this.ui.roll_dial.css('transform', 'rotate(' + int_roll + 'deg) scale(2)');
}
} else {
this.ui.roll_dial.css('transform', 'rotate(0deg) scale(2)');
Logger.warn('Invalid roll value received! Roll: ' + roll);
}
},
setHeading: function (heading) {
if (Validator.isValidHeading(heading)) {
this.ui.heading_text.text(Number(heading).toFixed(1));
var int_heading = Math.round(Number(heading));
if (int_heading !== this.current_heading) { //we only update the display if its something different
this.current_heading = int_heading;
this.ui.heading_dial.css('transform', 'rotate(' + int_heading + 'deg)');
}
} else {
this.ui.heading_dial.css('transform', 'rotate(' + 0 + 'deg)');
Logger.warn('Invalid heading value received! Heading: ' + heading);
}
},
setRollSetpoint: function (roll) {
if (Validator.isValidNumber(roll)) {
this.ui.roll_setpoint.text(Number(roll).toFixed(2));
}
else {
this.ui.roll_setpoint.text('Invalid');
Logger.warn('Invalid setRoll value received!! Roll: ' + roll);
}
},
setPitchSetpoint: function (pitch) {
if (Validator.isValidNumber(pitch)) {
this.ui.pitch_setpoint.text(Number(pitch).toFixed(2));
}
else {
this.ui.pitch_setpoint.text('Invalid');
Logger.warn('Invalid setPoll value received!! Pitch: ' + pitch);
}
},
setHeadingSetpoint: function (heading) {
if (Validator.isValidNumber(heading)) {
this.ui.heading_setpoint.text(Number(heading).toFixed(2));
}
else {
this.ui.heading_setpoint.text('Invalid');
Logger.warn('Invalid setHeading value received!! Heading: ' + heading);
}
},
sendSetPitch: function (e) {
e.preventDefault();
Commands.sendPitch(this.ui.pitch_input.val());
this.ui.pitch_input.val('');
},
sendSetRoll: function (e) {
e.preventDefault();
Commands.sendRoll(this.ui.roll_input.val());
this.ui.roll_input.val('');
},
sendSetHeading: function (e) {
e.preventDefault();
Commands.sendHeading(this.ui.heading_input.val());
this.ui.heading_input.val('');
}
});
};