/**
* @file Provides an interface between scripts and the RCbenchmark app
* @name RCbenchmark script API
* @author Tyto Robotics Inc. <[email protected]>
* @copyright 2015 Tyto Robotics Inc.
*/
//receive data from GUI
window.addEventListener('message', function(event) {
switch(event.data.command) {
case 'init':
rcb(event);
break;
case 'sensorReadCallback':
if(rcb.vars.callbacks.sensorRead) rcb.vars.callbacks.sensorRead(event.data.content);
rcb.vars.callbacks.sensorRead = undefined;
break;
case 'GUIerror':
rcb.console.error(event.data.content.message);
break;
case 'tareComplete':
if(rcb.vars.callbacks.tareComplete) rcb.vars.callbacks.tareComplete();
rcb.vars.callbacks.tareComplete = undefined;
break;
case 'updateSystemLimits':
rcb.vars.systemLimits = event.data.content;
break;
case 'ohmUpdate':
if(rcb.vars.callbacks.ohmRead) rcb.vars.callbacks.ohmRead(event.data.content);
rcb.vars.callbacks.ohmRead = undefined;
break;
case 'newLogEntryCallback':
if(rcb.vars.callbacks.newLogEntry) rcb.vars.callbacks.newLogEntry();
rcb.vars.callbacks.newLogEntry = undefined;
break;
case 'stop':
rcb.console.error('Script stopped by user.');
rcb.endScript();
break;
case 'pollUpdate':
//called everytime the usb has an update
if(rcb.vars.callbacks.outputRamp) rcb.vars.callbacks.outputRamp();
break;
// case 'somethingElse':
// ...
}
});
/**
* Api constructor. Called by the GUI to start the script (do not use in scripts).
* @param {Object} args - GUI arguments.
* @constructor
*/
var rcb = function (args) {
vars = {};
var cont = args.data.content;
vars.boardId = cont.config.boardId;
vars.boardVersion = cont.config.boardVersion;
vars.firmwareVersion = cont.config.firmwareVersion;
vars.sourcePage = args.source;
vars.sourceOrigin = args.origin;
vars.systemLimits = cont.system;
vars.userLimits = cont.user;
vars.output = cont.output;
vars.sensors = cont.sensors;
vars.printId = 0;
vars.verbose = true;
vars.callbacks = {};
rcb.vars = vars;
rcb.files.newLogFile.called = false;
//run the script
rcb.console.print('Started script: ' + cont.name + '.js');
try{
eval(cont.script); //run user script
}catch(e){
rcb.console.error(e.toString()); //error with user script -> show error on user console
}
};
/**
* Sends data back to the GUI
* @private
* @param {string} command - The command ID of the message being sent.
* @param {*} [content] - The content to send.
*/
rcb._sendGUIData = function (_command, _content) {
rcb.vars.sourcePage.postMessage({
command: _command,
content: _content
}, rcb.vars.sourceOrigin);
};
/**
* Console interface functions
* @class
*/
rcb.console = {
/**
* Prints a new line to the user console.
* @param {string} message - The string to print on the console. Can have html markup.
* @return {integer} A unique id, to reference this printed line later.
* @example
* rcb.console.print('Normal text');
* rcb.console.print('<strong>Bold font</strong>');
* rcb.console.print('<span style=\"color: green\">Green text</span>');
* @example
* //Simple examples
* rcb.console.print("I will always be there!");
* rcb.console.print("I will be gone");
* rcb.console.remove();
* rcb.console.append(" Still here!");
* rcb.console.print("I will be overwritten");
* rcb.console.overwrite("Fruits:");
*
* //Working with ids
* var id1 = rcb.console.print("I WILL BE REMOVED");
* var id2 = rcb.console.print("2");
* var id3 = rcb.console.print("I WILL BE OVERWRITTEN");
* rcb.console.print("3");
* rcb.console.append(" apples");
* rcb.console.append(" oranges", id2);
* rcb.console.remove(id1);
* rcb.console.overwrite("Doesn't compare with:", id3);
* rcb.endScript();
* @example
* //Animation example
* rcb.console.setVerbose(false);
* var lineQty = 12;
* var colQty = 25;
* var lines = [];
*
* //Create the lines
* for(var i=0; i<lineQty; i++) lines[i] = rcb.console.print("");
*
* //Starting coordinates
* var line = 0;
* var col = 0;
* var dirL = true;
* var dirC = true;
* //Travel loop until user stops script
* move();
* function move(){
* //Update coordinates
* if(dirL){
* line++;
* if (line === lineQty-1) dirL = false;
* }else{
* line--;
* if (line === 0) dirL = true;
* }
* if(dirC){
* col++;
* if (col === colQty-1) dirC = false;
* }else{
* col--;
* if (col === 0) dirC = true;
* }
*
* //Draw
* for(var i=0; i<lineQty; i++){
* if(i===line){
* var text = "";
* for(var j=0; j<col-1; j++) text+=" ";
* text+="O";
* rcb.console.overwrite(text,lines[i]);
* }else
* rcb.console.overwrite("",lines[i]);
* }
*
* //iterate
* rcb.wait(move,0.05);
* }
*/
print: function (_message) {
rcb.vars.printId++;
rcb._sendGUIData("print", {message:_message, id:rcb.vars.printId});
return rcb.vars.printId;
},
/**
* Adds to the last printed line (does not create a new line).
* @param {string} message - The string to print on the console. Can have html markup.
* @param {integer} [id] - If specified, will append to this line instead.
* @example
* rcb.console.print('Doing...');
* rcb.console.append('<strong>done</strong>');
*/
append: function (message, id) {
var params={};
params.message = message;
params.id = id;
rcb._sendGUIData("append", params);
},
/**
* Reprints over the last line of the user console. Useful to display progress updates.
* @param {string} message - The string to print on the console. Can have html markup.
* @param {integer} [id] - If specified, will overwrite this line instead.
*/
overwrite: function (message, id) {
var params={};
params.message = message;
params.id = id;
rcb._sendGUIData("overwrite", params);
},
/**
* Removes the last line of the user console.
* @param {integer} [id] - If specified, will remove this line instead.
*/
remove: function (id) {
rcb._sendGUIData("remove", id);
},
/**
* Prints a message in orange. Does not interrupt script.
* @param {string} message - The string to print on the console. Can have html markup.
* @return {integer} A unique id, to reference this printed line later.
* @example
* rcb.console.warning('<strong>Warning:</strong> winter is coming!');
*/
warning: function (message) {
return rcb.console.print('<span style=\"color: orange\">' + message + '</span>');
},
/**
* Prints an error on the user console. The script will be stopped.
* @param {string} message - The string to print on the console. Can have html markup.
* @example
* rcb.console.print("A basic message");
* rcb.console.warning("About to throw error");
* rcb.console.error("Bazinga!");
*/
error: function (message) {
rcb._sendGUIData("error", message);
rcb.endScript();
},
/**
* Prints if verbose mode is active
* @param {string} message - The string to print on the console. Can have html markup.
* @return {integer} A unique id, to reference this printed line later.
* @private
*/
_verbosePrint: function (message) {
if(rcb.vars.verbose){
return rcb.console.print('<span style=\"color: silver\">' + message + '</span>');
}else{
return false;
}
},
/**
* Reprints if verbose mode is active
* @param {string} message - The string to print on the console. Can have html markup.
* @param {integer} [id] - If specified, will overwrite this line instead.
* @private
*/
_verboseOverwrite: function (message, id) {
if(rcb.vars.verbose || id) rcb.console.overwrite('<span style=\"color: silver\">' + message + '</span>', id);
},
/**
* Appends if verbose mode is active
* @param {string} message - The string to print on the console. Can have html markup.
* @param {integer} [id] - If specified, will append this line instead.
* @private
*/
_verboseAppend: function (message, id) {
if(rcb.vars.verbose || id) rcb.console.append('<span style=\"color: silver\">' + message + '</span>', id);
},
/**
* Removes if verbose mode is active
* @param {integer} [id] - If specified, will remove this line instead.
* @private
*/
_verboseRemove: function (id) {
if(rcb.vars.verbose || id) rcb.console.remove(id);
},
/**
* Returns the verbose setting
* @return {boolean}
* @private
*/
_isVerbose: function () {
return(rcb.vars.verbose===true);
},
/**
* Sets the verbose mode for the rcb API. If activated (default), some API functions will print some text (in grey color). It may be useful to deactivate verbose mode in loops to avoid excessive text in the console.
* @param {boolean} value - If true, verbose mode is activated.
* @example
* //Example showing the effect of changing verbose mode
* rcb.console.print("Verbose activated...");
* rcb.setDebugMode(true);
* rcb.setDebugMode(false);
* rcb.console.setVerbose(false);
* rcb.console.print("Verbose deactivated...");
* rcb.setDebugMode(true);
* rcb.setDebugMode(false);
* rcb.endScript();
*/
setVerbose: function (value) {
rcb.vars.verbose = value;
}
};
/**
* Returns the board's unique ID.
* @return {string} A string representing the board's unique serial number.
* @example
* var boardId = rcb.getBoardId();
* rcb.console.print(boardId);
* rcb.endScript();
*/
rcb.getBoardId = function () {
return rcb.vars.boardId;
};
/**
* Returns the board's hardware version.
* @return {string} A string representing the board's hardware version.
*/
rcb.getBoardVersion = function () {
return rcb.vars.boardVersion;
};
/**
* Returns the board's firmware version.
* @return {string} A string representing the board's firmware version.
*/
rcb.getFirmwareVersion = function () {
return rcb.vars.firmwareVersion;
};
/**
* Finishes the script execution. If this function is not called, the user will have to press the "Stop" button to stop the script.
*/
rcb.endScript = function () {
rcb.console.print("Script finished");
clearTimeout(rcb.vars.callbacks.wait);
vars.callbacks = {};
rcb._sendGUIData("endScript");
};
/**
* Activates or deactivates the debug mode.
* @param {boolean} enable - Set to "true" to activate debug mode, "false" otherwise.
*/
rcb.setDebugMode = function (enable) {
if(enable){
rcb.console._verbosePrint("Enabling debug mode");
}else{
rcb.console._verbosePrint("Disabling debug mode");
}
rcb._sendGUIData("debugModeEnable",enable);
};
/**
* Output control interface functions.
* @class
*/
rcb.output = {
/**
* Directly sets the pwm output.
* @private
* @param {string} outputId - "esc", "servo1", "servo2", or "servo3"
* @param {number} pulseWidth - Pulse width between 1000 and 2000 microseconds.
* @param {boolean} [activate] - If specified, will change the active status.
*/
_control: function (outputId, pulseWidth, activate) {
if(pulseWidth===undefined) console.log.error("Missing pulseWidth parameter");
if(pulseWidth >= 1000 && pulseWidth <= 2000){
var act = !(activate===undefined);
switch (outputId.toLowerCase()){
case "esc":
rcb.vars.output.ESC_PWM = pulseWidth;
if(act) rcb.vars.output.active[0] = activate;
break;
case "servo1":
rcb.vars.output.Servo_PWM[0] = pulseWidth;
if(act) rcb.vars.output.active[1] = activate;
break;
case "servo2":
rcb.vars.output.Servo_PWM[1] = pulseWidth;
if(act) rcb.vars.output.active[2] = activate;
break;
case "servo3":
rcb.vars.output.Servo_PWM[2] = pulseWidth;
if(act) rcb.vars.output.active[3] = activate;
break;
default:
rcb.console.error('"' + outputId + '" is an invalid id (valid: "esc" "servo1" servo2" "servo3").');
break;
}
rcb._sendGUIData("control",rcb.vars.output);
}else
rcb.console.error("Out of range (1000-2000) pwm parameter: " + pulseWidth);
},
/**
* Controls the pwm signal outputs.
* @param {string} outputId - "esc", "servo1", "servo2", or "servo3"
* @param {number} pulseWidth - Pulse width between 1000 and 2000 microseconds. Set to 0 to disable the output signal.
* @example
* //Sets the motor at throttle 1300
* rcb.console.print("Allow time to initialize ESC");
* rcb.output.pwm("esc",1000);
* rcb.wait(callback, 4);
*
* function callback(){
* rcb.console.print("Run motor");
* rcb.output.pwm("esc",1300);
* rcb.wait(rcb.endScript, 5);
* }
*/
pwm: function (outputId, pulseWidth) {
if(pulseWidth===undefined) rcb.console.error("Missing pulseWidth parameter");
if(pulseWidth >= 1000 && pulseWidth <= 2000){
rcb.console._verbosePrint('Setting "' + outputId + '" to ' + pulseWidth);
rcb.output._control(outputId, pulseWidth, true);
}else if(pulseWidth === 0){
rcb.output._control(outputId, 1000, false);
} else
rcb.console.error("Out of range (1000-2000) pwm parameter: " + pulseWidth);
},
/**
* Callback for the rcb.output.ramp function.
* @callback rampDone
*/
/**
* Smoothly ramps up or down the pwm signal. For safety reasons, will only work if the output was previously activated using the pwm function.
* @param {string} outputId - "esc", "servo1", "servo2", or "servo3"
* @param {number} from - Ramp starting value between 1000 and 2000 microseconds.
* @param {number} to - Ramp finishing value between 1000 and 2000 microseconds.
* @param {number} duration - The duration of the ramp in seconds.
* @param {rampDone} callback - Function to execute when the ramp is finished.
* @example
* //Illustrates the use of the ramp function
* rcb.console.print("Initializing ESC...");
* rcb.output.pwm("esc",1000);
* rcb.wait(callback, 4);
*
* function callback(){
* var from = 1000;
* var to = 1400;
* var duration = 15;
* var done = rcb.endScript;
* rcb.output.ramp("esc", from, to, duration, done);
* }
*/
ramp: function (outputId, from, to, duration, callback) {
var startTime = window.performance.now()/1000
rcb.console._verbosePrint("Ramping " + outputId + " from " + from + " to " + to + " in " + duration + "s.");
var status = rcb.console._verbosePrint("");
//function to execute when sensors are updated
rcb.vars.callbacks.outputRamp = function(){
currTime = window.performance.now()/1000;
var outputVal;
var percentage;
var rampDone = false;
if(currTime - startTime >= duration){
//done with ramp, finish it
rcb.vars.callbacks.outputRamp = undefined;
outputVal = to;
percentage = 1.0;
rampDone = true;
}else{
//ramp in progress
percentage = (currTime-startTime)/duration;
outputVal = Math.round(percentage * (to-from) + from);
}
//output value
rcb.output._control(outputId, outputVal);
rcb.console._verboseOverwrite(" ramping " + (100*percentage).toFixed(1) + "%, val " + outputVal, status);
if(callback && rampDone) callback();
}
rcb.vars.callbacks.outputRamp();
},
/**
* Callback for the rcb.output.steps function.
* @callback stepDone
* @param {boolean} lastStep - Will be true if this is the last step.
* @param {function} nextStep - Function that you should call when ready to go to the next step.
*/
/**
* Steps up or down the pwm signal allowing you to perform tasks between each step. For safety reasons, will only work if the output was previously activated using the pwm function.
* @param {string} outputId - "esc", "servo1", "servo2", or "servo3"
* @param {number} from - Steps starting value between 1000 and 2000 microseconds.
* @param {number} to - Steps finishing value between 1000 and 2000 microseconds.
* @param {integer} steps - Number of steps to perform.
* @param {stepDone} [callback] - Function to execute when a step finishes. This function should introduce some sort of delay for the steps function to be effective.
* @example
* //Illustrates the use of the steps function
* rcb.console.print("Initializing ESC...");
* rcb.output.pwm("esc",1000);
* rcb.wait(callback, 4);
* var sensorPrintId;
*
* function callback(){
* var from = 1000;
* var to = 1400;
* var steps = 10;
* rcb.output.steps("esc", from, to, steps, stepFct);
* }
*
* //Function called at every step
* function stepFct(lastStep, nextStepFct){
* if(lastStep){
* rcb.endScript();
* }else{
* rcb.console.setVerbose(false);
* rcb.wait(function(){ //2 seconds settling time
*
* //Do stuff here... (record to log file, calculate something, etc...)
* rcb.sensors.read(readDone);
*
* }, 2);
* }
*
* //Function called when read complete
* function readDone(result){
* var speed = result.motorSpeed.displayValue;
* var unit = result.motorSpeed.displayUnit;
* if(sensorPrintId === undefined) sensorPrintId = rcb.console.print("");
* rcb.console.overwrite("Motor Speed: " + speed + " " + unit, sensorPrintId);
*
* //When done working, go to the next step
* rcb.console.setVerbose(true);
* nextStepFct();
* }
* }
*/
steps: function (outputId, from, to, steps, callback) {
//increment in steps
rcb.console._verbosePrint("Stepping " + outputId + " from " + from + " to " + to + " in " + steps + " steps.");
var status = rcb.console._verbosePrint("");
var currentStep = 0;
var step = function(){
var outputVal = from + Math.round((to-from)*(currentStep/steps));
rcb.console._verboseOverwrite(" step " + currentStep + " of " + steps + " (val: " + outputVal + ").", status);
rcb.output._control(outputId, outputVal);
if(currentStep<steps){
callback(false, step);
}else{
callback(true, function(){rcb.console.warning("No more steps!")});
}
currentStep++;
}
step();
},
};
/**
* Sensor interface functions.
* @class
*/
rcb.sensors = {
/**
* Callback for the readSensors function when readings are ready.
* @callback readSensorsReady
* @param {Object} results - Averaged reading results
* @param {function} results.print - Prints the content of the results structure
*/
/**
* Gets new sensor readings. Automatically averages a few readings for reducing noise. Please run the script in the second example to know the structure of the returned parameter.
* @param {readSensorsReady} callback - The function to execute when readings are ready.
* @param {integer} [averageQty=5] - The number of samples to average before returning the result.
* @example
* //Read 10 samples averaged, and print thrust on screen
* rcb.sensors.read(callback,10);
*
* function callback(result){
* var thrust = result.thrust.displayValue;
* var unit = result.thrust.displayUnit;
* rcb.console.print("Thrust: " + thrust.toPrecision(3) + " " + unit);
* rcb.endScript();
* }
* @example
* //This sample script prints the content of the structure
* //returned by the rcb.sensors.read callback
* rcb.sensors.read(callback);
*
* function callback(result){
* //print structure content
* result.print();
* rcb.endScript();
* }
*/
read: function (callback,averageQty) {
if(averageQty===undefined) averageQty = 5;
var status = rcb.console._verbosePrint("Reading and averaging next " + averageQty + " readings...");
rcb.vars.callbacks.sensorRead = function(result){
rcb.console._verboseAppend("done", status);
//append the print function to the structure
result.print = function(){
//print structure content
for (var key in result) {
if(key!="print"){
rcb.console.print('result.'+key);
for (var subkey in result[key]) {
rcb.console.print(' .' + subkey+' = '+result[key][subkey]);
}
rcb.console.print('');
}
}
}
if(callback)callback(result);
};
rcb._sendGUIData("sensorsRead",averageQty);
},
/**
* Callback for the readOhm function when reading is ready.
* @callback readOhmReady
* @param {number} result - Ohmmeter reading. If NaN means value is out of measurement range.
*/
/**
* Reads the ohmmeter. If verbose mode is active, the reading will be displayed on the console.
* @param {readOhmReady} [callback] - The function to execute when the reading is ready.
* @example
* //Gets the ohmmeter reading
* rcb.sensors.readOhm(callback);
*
* function callback(reading){
* rcb.console.print("Ohm reading: " + reading.toPrecision(4));
* rcb.endScript();
* }
*/
readOhm: function (callback) {
var status = rcb.console._verbosePrint('Reading ohmmeter...');
rcb.vars.callbacks.ohmRead = function(val){
rcb.console._verboseAppend(val.toPrecision(4), status);
if(callback)callback(val);
};
rcb._sendGUIData("readOhm","");
},
/**
* Callback for the tareLoadCells function when tare is complete.
* @callback tareComplete
*/
/**
* Performs a tare function on the load cells.
* @param {tareComplete} [callback] - The function to execute when the tare is complete.
* @example
* //Simple script that only tares the load cells and finishes when tare is complete.
* rcb.sensors.tareLoadCells(rcb.endScript);
*/
tareLoadCells: function (callback) {
var status = rcb.console._verbosePrint('Tare in progress...');
rcb.vars.callbacks.tareComplete = function(){
rcb.console._verboseAppend(' done', status);
if(callback)callback();
};
rcb._sendGUIData("tareLoadCells","");
},
/**
* Changes the safety limit for a sensor. Units are internal working units (A, V, RPM, g, and N·m) regardless of the user display units. It is not possible to set limits beyond hardware limits.
* @param {string} sensorId - "current", "voltage", "rpm", "thrust", or "torque".
* @param {number} min - Minimum sensor value before cutoff activates.
* @param {number} max - Maximum sensor value before cutoff activates.
* @example
* rcb.sensors.setSafetyLimit("current",10,20);
* //rcb.endScript -> the safety cutoff should stop the script
*/
setSafetyLimit: function (sensorId, min, max) {
setTimeout(function(){ //Timout required to ensure the number of poles propagates the updated system limits...
var id = sensorId.toLowerCase();
var capitalized = id.charAt(0).toUpperCase() + id.substring(1);
var minSystem = rcb.vars.systemLimits[sensorId+'Min'];
var maxSystem = rcb.vars.systemLimits[sensorId+'Max'];
if(id==="current" || id==="voltage" || id==="rpm" || id==="thrust" || id==="torque"){
if(min<max){
if(min >= minSystem && max <= maxSystem){
rcb.vars.userLimits[sensorId+'Min'] = min;
rcb.vars.userLimits[sensorId+'Max'] = max;
rcb.console._verbosePrint(capitalized + ' limits set to ['+min+' - '+max+']');
rcb._sendGUIData("changeSafetyLimits",rcb.vars.userLimits);
} else rcb.console.error(capitalized + ': ['+min+' - '+max+'] values exceed system limits ['+minSystem+' - '+maxSystem+']');
}else rcb.console.error('Invalid safety limits input range ['+min+' - '+max+']');
}else{
rcb.console.error('Unrecognized sensorId: ' + sensorId + '. Accepted values are "current", "voltage", "rpm", "thrust", "torque".');
}
},20);
},
/**
* Changes the number of motor poles. The correct number of poles is required to obtain a correct rpm reading.
* @param {integer} numberOfPoles - The motor number of poles. Must be an multiple of 2.
* @example
* rcb.sensors.setMotorPoles(6);
*/
setMotorPoles: function (numberOfPoles) {
if(numberOfPoles>0 && numberOfPoles%2===0){
rcb.console._verbosePrint('Setting motor poles to ' + numberOfPoles);
rcb._sendGUIData("setPoles",numberOfPoles);
}else
rcb.console.error("Invalid number of poles: " + numberOfPoles);
},
/**
* Allows to completely disable safety limits (dangerous function).
* @private
* @param {boolean} enable - Set to "false" to disable safety limit check.
*/
_setSafetyEnable: function (enable) {
if(!enable){
rcb.console.warning("Warning: disabling safety cutoffs!");
}else{
rcb.console._verbosePrint('Safety cutoffs enabled.');
}
rcb._sendGUIData("safetyCutoffDisable",!enable);
}
};
/**
* Callback for the wait function.
* @callback waitDone
*/
/**
* Waits a certain number of seconds before executing the callback function.
* @param {waitDone} callback - The function to execute after the delay is over.
* @param {number} delay - Wait delay in seconds (can be floating numbers like 0.1 for 100ms).
* @example
* //Illustrates the use of the wait and overwrite functions
* rcb.console.print("LEGEND...");
* rcb.console.setVerbose(false);
* rcb.wait(callback1, 2);
*
* function callback1(){
* rcb.console.overwrite("LEGEND... wait for it...");
* rcb.wait(callback2, 2);
* }
*
* function callback2(){
* rcb.console.overwrite("LEGEND... wait for it... DARY!");
* rcb.wait(callback3, 1.5);
* }
*
* function callback3(){
* rcb.console.overwrite("LEGENDARY!");
* rcb.endScript();
* }
*/
rcb.wait = function (callback,delay) {
var s = "s";
if(delay===1) s = "";
var status = rcb.console._verbosePrint("Waiting " + delay + " second" + s + "...");
clearTimeout(rcb.vars.callbacks.wait);
rcb.vars.callbacks.wait = setTimeout(function(){
rcb.console._verboseAppend("done", status);
if(callback) callback();
}, delay*1000);
};
/**
* File interface functions
* @class
*/
rcb.files = {
/**
* Creates a new file for logging (in the user's working directory). Generates an error if user's working directory is not set.
* @param {Object} [params] - parameters for file creation.
* @param {string} [params.prefix="Auto"] - A prefix to append in front of the file.
* @param {Array.<string>} [params.additionalHeaders] - Additional header(s) in addition to the standard headers.
* @example
* //Example script recording 5 rows in a loop
* var numberOflines = 5;
*
* //Create a new log
* rcb.files.newLogFile({prefix: "Example1"});
*
* //Start the sequence
* readSensor();
*
* function readSensor(){
* if(numberOflines>0){
* rcb.sensors.read(readDone);
* numberOflines--;
* }else
* rcb.endScript();
* }
*
* function readDone(result){
* rcb.files.newLogEntry(result,readSensor);
* }
* @example
* //Example recording with extra data
* var numberOfLines = 5;
*
* //Create a new log
* var add = ["Remaining", "Line"];
* rcb.files.newLogFile({prefix: "Example2", additionalHeaders: add});
*
* //Start the sequence
* readSensor();
*
* function readSensor(){
* if(numberOfLines>0){
* rcb.sensors.read(readDone);
* numberOfLines--;
* }else
* rcb.endScript();
* }
*
* function readDone(result){
* var add = [numberOfLines, 5-numberOfLines];
* rcb.files.newLogEntry(result,readSensor,add);
* }
* @example
* //This example continuously records data at full
* //speed until the user stops the script
*
* //Create a new log
* rcb.files.newLogFile({prefix: "Continuous"});
*
* //Start the sequence
* readSensor();
*
* function readSensor(){
* rcb.sensors.read(saveResult,10);
* }
*
* function saveResult(result){
* rcb.files.newLogEntry(result, readSensor);
* }
*/
newLogFile: function (params) {
if(params===undefined) params={};
if(params.prefix===undefined) params.prefix = "Auto";
rcb._sendGUIData("newLogFile",params);
rcb.files.newLogFile.called = true;
rcb.console._verbosePrint('Creating new log file with "' + params.prefix + '" prefix in working directory');
},
/**
* Callback for the newLogEntry function when finished writing to file.
* @callback newLogSaved
*/
/**
* Records a new entry to the created log file. Generates an error if the function newLogFile was never called.
* @param {Object} readings - Sensor readings as returned by the readSensors function.
* @param {newLogSaved} callback - The function to execute when recording is done.
* @param {Array.<number|string>} [additionalValues] - Additional values to append to the entry.
*/
newLogEntry: function (readings,callback,additionalValues) {
delete readings.print;
if(!rcb.files.newLogFile.called) rcb.console.error("newLogFile function must be called before calling the newLogEntry function");
else{
var status = rcb.console._verbosePrint("Saving new log entry...");
rcb.vars.callbacks.newLogEntry = function(){
rcb.console._verboseAppend("done", status);
if(callback) callback();
}
params={};
params.data = readings;
params.additionalValues = additionalValues;
rcb._sendGUIData("newLogEntry",params);
}
},
};