1
0
Fork 0
mirror of https://github.com/iNavFlight/inav-configurator.git synced 2025-07-14 20:10:11 +03:00
inav-configurator/js/backup_restore.js
Dominic Clifton 687c0f1a84 Remove legacy servo configuration which originated from the overloaded
use of the servo 'rate'.  All servos are free to be configured
regardless of whether the mixer actually uses those servos.
2015-07-13 02:01:19 +01:00

638 lines
No EOL
24 KiB
JavaScript

'use strict';
// code below is highly experimental, although it runs fine on latest firmware
// the data inside nested objects needs to be verified if deep copy works properly
function configuration_backup(callback) {
var activeProfile = null,
profilesN = 3;
var configuration = {
'generatedBy': chrome.runtime.getManifest().version,
'apiVersion': CONFIG.apiVersion,
'profiles': [],
};
MSP.send_message(MSP_codes.MSP_STATUS, false, false, function () {
activeProfile = CONFIG.profile;
select_profile();
});
function select_profile() {
if (activeProfile > 0) {
MSP.send_message(MSP_codes.MSP_SELECT_SETTING, [0], false, fetch_specific_data);
} else {
fetch_specific_data();
}
}
var profileSpecificData = [
MSP_codes.MSP_PID_CONTROLLER,
MSP_codes.MSP_PID,
MSP_codes.MSP_RC_TUNING,
MSP_codes.MSP_ACC_TRIM,
MSP_codes.MSP_SERVO_CONFIGURATIONS,
MSP_codes.MSP_MODE_RANGES,
MSP_codes.MSP_ADJUSTMENT_RANGES
];
function update_profile_specific_data_list() {
if (semver.lt(CONFIG.apiVersion, "1.12.0")) {
profileSpecificData.push(MSP_codes.MSP_CHANNEL_FORWARDING);
} else {
profileSpecificData.push(MSP_codes.MSP_SERVO_RULES);
}
}
update_profile_specific_data_list();
function fetch_specific_data() {
var fetchingProfile = 0,
codeKey = 0;
function fetch_specific_data_item() {
if (fetchingProfile < profilesN) {
MSP.send_message(profileSpecificData[codeKey], false, false, function () {
codeKey++;
if (codeKey < profileSpecificData.length) {
fetch_specific_data_item();
} else {
configuration.profiles.push({
'PID': jQuery.extend(true, {}, PID),
'PIDs': jQuery.extend(true, [], PIDs),
'RC': jQuery.extend(true, {}, RC_tuning),
'AccTrim': jQuery.extend(true, [], CONFIG.accelerometerTrims),
'ServoConfig': jQuery.extend(true, [], SERVO_CONFIG),
'ServoRules': jQuery.extend(true, [], SERVO_RULES),
'ModeRanges': jQuery.extend(true, [], MODE_RANGES),
'AdjustmentRanges': jQuery.extend(true, [], ADJUSTMENT_RANGES)
});
codeKey = 0;
fetchingProfile++;
MSP.send_message(MSP_codes.MSP_SELECT_SETTING, [fetchingProfile], false, fetch_specific_data_item);
}
});
} else {
MSP.send_message(MSP_codes.MSP_SELECT_SETTING, [activeProfile], false, fetch_unique_data);
}
}
// start fetching
fetch_specific_data_item();
}
var uniqueData = [
MSP_codes.MSP_MISC,
MSP_codes.MSP_RX_MAP,
MSP_codes.MSP_BF_CONFIG,
MSP_codes.MSP_CF_SERIAL_CONFIG,
MSP_codes.MSP_LED_STRIP_CONFIG
];
function update_unique_data_list() {
if (semver.gte(CONFIG.apiVersion, "1.8.0")) {
uniqueData.push(MSP_codes.MSP_LOOP_TIME);
uniqueData.push(MSP_codes.MSP_ARMING_CONFIG);
}
}
update_unique_data_list();
function fetch_unique_data() {
var codeKey = 0;
function fetch_unique_data_item() {
if (codeKey < uniqueData.length) {
MSP.send_message(uniqueData[codeKey], false, false, function () {
codeKey++;
fetch_unique_data_item();
});
} else {
configuration.MISC = jQuery.extend(true, {}, MISC);
configuration.RCMAP = jQuery.extend(true, [], RC_MAP);
configuration.BF_CONFIG = jQuery.extend(true, {}, BF_CONFIG);
configuration.SERIAL_CONFIG = jQuery.extend(true, {}, SERIAL_CONFIG);
configuration.LED_STRIP = jQuery.extend(true, [], LED_STRIP);
if (semver.gte(CONFIG.apiVersion, "1.8.0")) {
configuration.FC_CONFIG = jQuery.extend(true, {}, FC_CONFIG);
configuration.ARMING_CONFIG = jQuery.extend(true, {}, ARMING_CONFIG);
}
save();
}
}
// start fetching
fetch_unique_data_item();
}
function save() {
var chosenFileEntry = null;
var accepts = [{
extensions: ['txt']
}];
// generate timestamp for the backup file
var d = new Date(),
now = (d.getMonth() + 1) + '.' + d.getDate() + '.' + d.getFullYear() + '.' + d.getHours() + '.' + d.getMinutes();
// create or load the file
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: 'cleanflight_backup_' + now, accepts: accepts}, function (fileEntry) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
return;
}
if (!fileEntry) {
console.log('No file selected, backup aborted.');
return;
}
chosenFileEntry = fileEntry;
// echo/console log path specified
chrome.fileSystem.getDisplayPath(chosenFileEntry, function (path) {
console.log('Backup file path: ' + path);
});
// change file entry from read only to read/write
chrome.fileSystem.getWritableEntry(chosenFileEntry, function (fileEntryWritable) {
// check if file is writable
chrome.fileSystem.isWritableEntry(fileEntryWritable, function (isWritable) {
if (isWritable) {
chosenFileEntry = fileEntryWritable;
// crunch the config object
var serialized_config_object = JSON.stringify(configuration);
var blob = new Blob([serialized_config_object], {type: 'text/plain'}); // first parameter for Blob needs to be an array
chosenFileEntry.createWriter(function (writer) {
writer.onerror = function (e) {
console.error(e);
};
var truncated = false;
writer.onwriteend = function () {
if (!truncated) {
// onwriteend will be fired again when truncation is finished
truncated = true;
writer.truncate(blob.size);
return;
}
console.log('Write SUCCESSFUL');
if (callback) callback();
};
writer.write(blob);
}, function (e) {
console.error(e);
});
} else {
// Something went wrong or file is set to read only and cannot be changed
console.log('File appears to be read only, sorry.');
}
});
});
});
}
}
function configuration_restore(callback) {
var chosenFileEntry = null;
var accepts = [{
extensions: ['txt']
}];
// load up the file
chrome.fileSystem.chooseEntry({type: 'openFile', accepts: accepts}, function (fileEntry) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
return;
}
if (!fileEntry) {
console.log('No file selected, restore aborted.');
return;
}
chosenFileEntry = fileEntry;
// echo/console log path specified
chrome.fileSystem.getDisplayPath(chosenFileEntry, function (path) {
console.log('Restore file path: ' + path);
});
// read contents into variable
chosenFileEntry.file(function (file) {
var reader = new FileReader();
reader.onprogress = function (e) {
if (e.total > 1048576) { // 1 MB
// dont allow reading files bigger then 1 MB
console.log('File limit (1 MB) exceeded, aborting');
reader.abort();
}
};
reader.onloadend = function (e) {
if (e.total != 0 && e.total == e.loaded) {
console.log('Read SUCCESSFUL');
try { // check if string provided is a valid JSON
var configuration = JSON.parse(e.target.result);
} catch (e) {
// data provided != valid json object
console.log('Data provided != valid JSON string, restore aborted.');
return;
}
// validate
if (typeof configuration.generatedBy !== 'undefined' && compareVersions(configuration.generatedBy, CONFIGURATOR.backupFileMinVersionAccepted)) {
if (!migrate(configuration)) {
GUI.log(chrome.i18n.getMessage('backupFileUnmigratable'));
return;
}
configuration_upload(configuration, callback);
} else {
GUI.log(chrome.i18n.getMessage('backupFileIncompatible'));
}
}
};
reader.readAsText(file);
});
});
function compareVersions(generated, required) {
if (generated == undefined) {
return false;
}
return semver.gte(generated, required);
}
function migrate(configuration) {
var appliedMigrationsCount = 0;
var migratedVersion = configuration.generatedBy;
GUI.log(chrome.i18n.getMessage('configMigrationFrom', [migratedVersion]));
if (!compareVersions(migratedVersion, '0.59.1')) {
// variable was renamed
configuration.MISC.rssi_channel = configuration.MISC.rssi_aux_channel;
configuration.MISC.rssi_aux_channel = undefined;
migratedVersion = '0.59.1';
GUI.log(chrome.i18n.getMessage('configMigratedTo', [migratedVersion]));
appliedMigrationsCount++;
}
if (!compareVersions(migratedVersion, '0.60.1')) {
// LED_STRIP support was added.
if (!configuration.LED_STRIP) {
configuration.LED_STRIP = [];
}
migratedVersion = '0.60.1';
GUI.log(chrome.i18n.getMessage('configMigratedTo', [migratedVersion]));
appliedMigrationsCount++;
}
if (!compareVersions(migratedVersion, '0.61.0')) {
// Changing PID controller via UI was added.
if (!configuration.PIDs && configuration.PID) {
configuration.PIDs = configuration.PID;
configuration.PID = {
controller: 0 // assume pid controller 0 was used.
};
}
migratedVersion = '0.61.0';
GUI.log(chrome.i18n.getMessage('configMigratedTo', [migratedVersion]));
appliedMigrationsCount++;
}
if (!compareVersions(migratedVersion, '0.63.0')) {
// LED Strip was saved as object instead of array.
if (typeof(configuration.LED_STRIP) == 'object') {
var fixed_led_strip = [];
var index = 0;
while (configuration.LED_STRIP[index]) {
fixed_led_strip.push(configuration.LED_STRIP[index++]);
}
configuration.LED_STRIP = fixed_led_strip;
}
for (var profileIndex = 0; profileIndex < 3; profileIndex++) {
var RC = configuration.profiles[profileIndex].RC;
// TPA breakpoint was added
if (!RC.dynamic_THR_breakpoint) {
RC.dynamic_THR_breakpoint = 1500; // firmware default
}
// Roll and pitch rates were split
RC.roll_rate = RC.roll_pitch_rate;
RC.pitch_rate = RC.roll_pitch_rate;
}
migratedVersion = '0.63.0';
GUI.log(chrome.i18n.getMessage('configMigratedTo', [migratedVersion]));
appliedMigrationsCount++;
}
if (configuration.apiVersion == undefined) {
configuration.apiVersion = "1.0.0" // a guess that will satisfy the rest of the code
}
// apiVersion previously stored without patchlevel
if (!semver.parse(configuration.apiVersion)) {
configuration.apiVersion += ".0";
if (!semver.parse(configuration.apiVersion)) {
return false;
}
}
if (compareVersions(migratedVersion, '0.63.0') && !compareVersions(configuration.apiVersion, '1.7.0')) {
// Serial configuation redesigned, 0.63.0 saves old and new configurations.
var ports = [];
for (var portIndex = 0; portIndex < configuration.SERIAL_CONFIG.ports.length; portIndex++) {
var oldPort = configuration.SERIAL_CONFIG.ports[portIndex];
var newPort = {
identifier: oldPort.identifier,
functions: [],
msp_baudrate: String(configuration.SERIAL_CONFIG.mspBaudRate),
gps_baudrate: String(configuration.SERIAL_CONFIG.gpsBaudRate),
telemetry_baudrate: 'AUTO',
blackbox_baudrate: '115200',
};
switch(oldPort.scenario) {
case 1: // MSP, CLI, TELEMETRY, SMARTPORT TELEMETRY, GPS-PASSTHROUGH
case 5: // MSP, CLI, GPS-PASSTHROUGH
case 8: // MSP ONLY
newPort.functions.push('MSP');
break;
case 2: // GPS
newPort.functions.push('GPS');
break;
case 3: // RX_SERIAL
newPort.functions.push('RX_SERIAL');
break;
case 10: // BLACKBOX ONLY
newPort.functions.push('BLACKBOX');
break;
case 11: // MSP, CLI, BLACKBOX, GPS-PASSTHROUGH
newPort.functions.push('MSP');
newPort.functions.push('BLACKBOX');
break;
}
ports.push(newPort);
}
configuration.SERIAL_CONFIG = {
ports: ports
};
appliedMigrationsCount++;
}
if (compareVersions(migratedVersion, '0.63.0') && !compareVersions(configuration.apiVersion, '1.8.0')) {
// api 1.8 exposes looptime and arming config
if (configuration.FC_CONFIG == undefined) {
configuration.FC_CONFIG = {
loopTime: 3500
};
}
if (configuration.ARMING_CONFIG == undefined) {
configuration.ARMING_CONFIG = {
auto_disarm_delay: 5,
disarm_kill_switch: 1
};
}
appliedMigrationsCount++;
}
if (compareVersions(migratedVersion, '0.63.0')) {
// backups created with 0.63.0 for firmwares with api < 1.8 were saved with incorrect looptime
if (configuration.FC_CONFIG.loopTime == 0) {
//reset it to the default
configuration.FC_CONFIG.loopTime = 3500;
}
}
if (semver.lt(migratedVersion, '0.66.0')) {
// api 1.12 updated servo configuration protocol and added servo mixer rules
for (var profileIndex = 0; i < configuration.profiles.length; i++) {
if (semver.eq(configuration.apiVersion, '1.10.0')) {
// drop two unused servo configurations
while (configuration.profiles[profileIndex].ServoConfig.length > 8) {
configuration.profiles[profileIndex].ServoConfig.pop();
}
}
for (var i = 0; i < configuration.profiles[profileIndex].ServoConfig.length; i++) {
var servoConfig = profiles[profileIndex].ServoConfig;
servoConfig[i].angleAtMin = 90;
servoConfig[i].angleAtMax = 90;
servoConfig[i].reversedInputSources = 0;
// set the rate to 0 if an invalid value is detected.
if (servoConfig[i].rate < -100 || servoConfig[i].rate > 100) {
servoConfig[i].rate = 0;
}
}
configuration.profiles[profileIndex].ServoRules = [];
}
migratedVersion = '0.66.0';
appliedMigrationsCount++;
}
if (appliedMigrationsCount > 0) {
GUI.log(chrome.i18n.getMessage('configMigrationSuccessful', [appliedMigrationsCount]));
}
return true;
}
function configuration_upload(configuration, callback) {
function upload() {
var activeProfile = null,
profilesN = 3;
var profileSpecificData = [
MSP_codes.MSP_SET_PID_CONTROLLER,
MSP_codes.MSP_SET_PID,
MSP_codes.MSP_SET_RC_TUNING,
MSP_codes.MSP_SET_ACC_TRIM
];
MSP.send_message(MSP_codes.MSP_STATUS, false, false, function () {
activeProfile = CONFIG.profile;
select_profile();
});
function select_profile() {
if (activeProfile > 0) {
MSP.send_message(MSP_codes.MSP_SELECT_SETTING, [0], false, upload_specific_data);
} else {
upload_specific_data();
}
}
function upload_specific_data() {
var savingProfile = 0,
codeKey = 0;
function load_objects(profile) {
PID = configuration.profiles[profile].PID;
PIDs = configuration.profiles[profile].PIDs;
RC_tuning = configuration.profiles[profile].RC;
CONFIG.accelerometerTrims = configuration.profiles[profile].AccTrim;
SERVO_CONFIG = configuration.profiles[profile].ServoConfig;
SERVO_RULES = configuration.profiles[profile].ServoRules;
MODE_RANGES = configuration.profiles[profile].ModeRanges;
ADJUSTMENT_RANGES = configuration.profiles[profile].AdjustmentRanges;
}
function upload_using_specific_commands() {
MSP.send_message(profileSpecificData[codeKey], MSP.crunch(profileSpecificData[codeKey]), false, function () {
codeKey++;
if (codeKey < profileSpecificData.length) {
upload_using_specific_commands();
} else {
codeKey = 0;
savingProfile++;
if (savingProfile < profilesN) {
load_objects(savingProfile);
MSP.send_message(MSP_codes.MSP_EEPROM_WRITE, false, false, function () {
MSP.send_message(MSP_codes.MSP_SELECT_SETTING, [savingProfile], false, upload_using_specific_commands);
});
} else {
MSP.send_message(MSP_codes.MSP_EEPROM_WRITE, false, false, function () {
MSP.send_message(MSP_codes.MSP_SELECT_SETTING, [activeProfile], false, upload_unique_data);
});
}
}
});
}
function upload_servo_mix_rules() {
MSP.sendServoMixRules(upload_servo_configuration);
}
function upload_servo_configuration() {
MSP.sendServoConfigurations(upload_mode_ranges);
}
function upload_mode_ranges() {
MSP.sendModeRanges(upload_adjustment_ranges);
}
function upload_adjustment_ranges() {
MSP.sendAdjustmentRanges(upload_using_specific_commands);
}
// start uploading
load_objects(0);
upload_servo_configuration();
}
function upload_unique_data() {
var codeKey = 0;
var uniqueData = [
MSP_codes.MSP_SET_MISC,
MSP_codes.MSP_SET_RX_MAP,
MSP_codes.MSP_SET_BF_CONFIG,
MSP_codes.MSP_SET_CF_SERIAL_CONFIG
];
function update_unique_data_list() {
if (semver.gte(CONFIG.apiVersion, "1.8.0")) {
uniqueData.push(MSP_codes.MSP_SET_LOOP_TIME);
uniqueData.push(MSP_codes.MSP_SET_ARMING_CONFIG);
}
}
function load_objects() {
MISC = configuration.MISC;
RC_MAP = configuration.RCMAP;
BF_CONFIG = configuration.BF_CONFIG;
SERIAL_CONFIG = configuration.SERIAL_CONFIG;
LED_STRIP = configuration.LED_STRIP;
ARMING_CONFIG = configuration.ARMING_CONFIG;
FC_CONFIG = configuration.FC_CONFIG;
}
function send_unique_data_item() {
if (codeKey < uniqueData.length) {
MSP.send_message(uniqueData[codeKey], MSP.crunch(uniqueData[codeKey]), false, function () {
codeKey++;
send_unique_data_item();
});
} else {
MSP.send_message(MSP_codes.MSP_EEPROM_WRITE, false, false, send_led_strip_config);
}
}
load_objects();
update_unique_data_list();
// start uploading
send_unique_data_item();
}
function send_led_strip_config() {
MSP.sendLedStripConfig(reboot);
}
function reboot() {
GUI.log(chrome.i18n.getMessage('eeprom_saved_ok'));
GUI.tab_switch_cleanup(function() {
MSP.send_message(MSP_codes.MSP_SET_REBOOT, false, false, reinitialize);
});
}
function reinitialize() {
GUI.log(chrome.i18n.getMessage('deviceRebooting'));
GUI.timeout_add('waiting_for_bootup', function waiting_for_bootup() {
MSP.send_message(MSP_codes.MSP_IDENT, false, false, function () {
GUI.log(chrome.i18n.getMessage('deviceReady'));
if (callback) callback();
});
}, 1500); // 1500 ms seems to be just the right amount of delay to prevent data request timeouts
}
}
upload();
}
}