Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • anders_blomdell/labcomm
  • klaren/labcomm
  • tommyo/labcomm
  • erikj/labcomm
  • sven/labcomm
5 results
Select Git revision
Loading items
Show changes
Commits on Source (8)
......@@ -5,3 +5,4 @@ tech_report.fdb_latexmk
tech_report.fls
tech_report.log
tech_report.pdf
tech_report.synctex.gz
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>title</title>
<script src="lc.js"></script>
</head>
<body>
<input id="url" type="text" />
<button id="btn">Get!</button>
<!-- page content -->
<script>
function decodeFromUrl(url) {
var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
oReq.responseType = "arraybuffer";
oReq.onload = function (oEvent) {
var arrayBuffer = oReq.response;
if (arrayBuffer) {
var buff = new LabComm.Buffer(new DataView(arrayBuffer));
window.decoder = new LabComm.Decoder(function(decl, data) {
testObject(data);
});
window.decoder.decodePackage(buff);
}
};
oReq.send(null);
}
var urlInp = document.getElementById('url');
var btn = document.getElementById('btn');
btn.onclick = function() {
decodeFromUrl(urlInp.value);
};
</script>
<script src='tester.js'></script>
</body>
</html>
'use strict';
var LabComm = LabComm || {};
/**
* The maximum "safe" integer that can be stroed in a double
* @define {number}
*/
var MAX_INT = 0x1fffffffffffff;
/**
* Constants used during parsing
* @enum {number}
*/
LabComm.Constants = {
VERSION: 0x01,
SAMPLE_DEF: 0x02,
SAMPLE_REF: 0x03,
TYPE_DEF: 0x04,
TYPE_BINDING: 0x05,
PRAGMA: 0x3f,
ARRAY: 0x10,
STRUCT: 0x11,
BOOLEAN: 0x20,
BYTE: 0x21,
SHORT: 0x22,
INT: 0x23,
LONG: 0x24,
FLOAT: 0x25,
DOUBLE: 0x26,
STRING: 0x27
};
/**
* A buffer objects that supports all labcomm types
* @constructor
* @param {DataView} dataView An underlying dataview to work on
* @param {number=} offset An initial offset, defaults to 0
*/
LabComm.Buffer = function (dataView, offset) {
/**
* The current position in the buffer
* @type {number}
* @private
*/
this.offset_ = offset || 0;
/**
* The underlying dataview
* @type {DataView}
* @private
*/
this.data_ = dataView;
/**
* The original offset, used to rewind
* @type {number}
* @private
*/
this.originalOffset_ = this.offset_;
};
/**
* Check if there is more data that can be red
* @return {boolean} True if there is more data left
*/
LabComm.Buffer.prototype.hasRemaning = function() {
return this.offset_ < this.data_.byteLength;
};
/**
* Returns the remaning number of bytes
* @return {number} The number of bytes left
*/
LabComm.Buffer.prototype.remaning = function() {
return this.data_.byteLength - this.offset_;
};
/**
* Sets the position to the beginning of the buffer
*/
LabComm.Buffer.prototype.rewind = function() {
this.offset_ = this.originalOffset_;
};
/**
* Reads a var-int from the buffer.
* @return {number} An integer
*/
LabComm.Buffer.prototype.getVarint = function() {
var nextByte, result = 0, bytesRead = 0;
do {
nextByte = this.data_.getUint8(this.offset_+bytesRead);
result += (nextByte & 0x7F) << (7 * bytesRead);
bytesRead++;
} while (nextByte >= 0x80)
this.offset_ += bytesRead;
return result;
};
/**
* Reads a string from the buffer.
* @return {string} A string
*/
LabComm.Buffer.prototype.getString = function() {
var len = this.getVarint();
var chars = new Uint8Array(len);
for(var i = 0; i < len; i++) {
chars[i] = this.data_.getUint8(this.offset_ + i);
}
var str = String.fromCharCode.apply(null, chars);
this.offset_ += len;
return str;
};
/**
* Reads a boolean (8-bits) from the buffer.
* @return {boolean} A boolean
*/
LabComm.Buffer.prototype.getBoolean = function() {
return Boolean(this.data_.getUint8(this.offset_++));
};
/**
* Reads a byte (8-bits) from the buffer.
* @return {number} A byte
*/
LabComm.Buffer.prototype.getByte = function() {
return this.data_.getUint8(this.offset_++);
};
/**
* Reads a short (16-bits) from the buffer.
* @return {number} A short
*/
LabComm.Buffer.prototype.getShort = function() {
var s = this.data_.getInt16(this.offset_);
this.offset_ += 2;
return s;
};
/**
* Reads an interger (32-bits) from the buffer.
* @return {number} An integer
*/
LabComm.Buffer.prototype.getInt = function() {
var s = this.data_.getInt32(this.offset_);
this.offset_ += 4;
return s;
};
/**
* Reads a long (64-bits) from the buffer, note that JavaScript only has
* doubles internaly so if the long requires more than 53-bits the number
* will fall over to +/- Infinity.
* @return {number} A long
*/
LabComm.Buffer.prototype.getLong = function() {
var low = this.data_.getInt32(this.offset_ + 4);
var n = this.data_.getInt32(this.offset_) * 0x100000000 + low;
if (low < 0) n += 0x100000000;
this.offset_ += 8;
if(Math.abs(n) > MAX_INT) {
return n < 0 ? -Infinity : Infinity;
}
return n;
};
/**
* Reads a float (32-bits) from the buffer.
* @return {number} A float
*/
LabComm.Buffer.prototype.getFloat = function() {
var s = this.data_.getFloat32(this.offset_);
this.offset_ += 4;
return s;
};
/**
* Reads a double (64-bits) from the buffer.
* @return {number} A double
*/
LabComm.Buffer.prototype.getDouble = function() {
var s = this.data_.getFloat64(this.offset_);
this.offset_ += 8;
return s;
};
/**
* Skip a number of bytes
* @param {number} length The number of bytes to skip
*/
LabComm.Buffer.prototype.skip = function(length) {
this.offset_ += length;
};
/**
* A parser that cad decode LabComm binary data
* @constructor
* @param {function(LabComm.SampleDeclaration, Object)=} callback A callback that will get all the decoded samples
*/
LabComm.Decoder = function(callback) {
/**
* The sample declarations that has been registred so far
* @type {Object<number, LabComm.SampleDeclaration>}
* @private
*/
this.declarations_ = {};
/**
* The callback that will recieve all samples and declarations
* @type {function(LabComm.SampleDeclaration, Object)}
* @private
*/
this.callback_ = callback || function(a,b) {};
/**
* A unique id for variable naming inside the decoder functions
* @type {number}
* @private
*/
this.uid_ = 1;
};
/**
* Builds a part of the decoder function
* @param {string} name The name of the current field
* @param {Array<string>} json The final json representation of the fields
* @param {Array<string>} fb The final function body of the decoder
* @param {string} type The string representation of the type
* @param {string} parser The parsing function to be called when decoding current field
* @param {number} level The current nesting level
* @param {string=} opt_parentArray The array variable to put the result in
* @return {string} The JSON part of the final structure
* @private
*/
LabComm.Decoder.prototype.buildDecoder_ = function(name, json, fb, type, parser, level, opt_parentArray) {
if(opt_parentArray) {
fb.push(this.indent_(level) + opt_parentArray + ' = ' + parser + '; //'+name+'\n');
return type;
}
var varname = 'v'+this.uid_++;
json.push('"'+name+'":'+varname);
fb.push(this.indent_(level) + 'var '+varname+' = ' + parser + '; //'+name+'\n');
return '"'+name+'":'+'"'+type+'"';
};
/**
* Creates indentation
* @param {number} level The indentation level to match
* @return {string} A string with the given level of indentation
* @private
*/
LabComm.Decoder.prototype.indent_ = function(level) {
return new Array(level + 1).join('\t');
};
/**
* Splits the variable name based on array literal
* e.g. int[3] => {type: "int", dim: "[3]"}
* @param {string} str The string representation of an array type
* @return {{type: string, dim: string}} An object consisting of the split string
*/
LabComm.Decoder.prototype.exctractArray_ = function(str) {
var index = str.indexOf('[');
if(index > 0) {
return {type: str.substring(0,index), dim: str.substring(index)};
}
return {type: str, dim: ''};
}
/**
* Decodes a signature recursively
* @param {string} name The name of the current field
* @param {Array<string>} json The final json representation of the fields
* @param {Array<string>} fb The final function body of the decoder
* @param {number} level The current nestling level
* @param {string=} opt_parentArray The array variable to put the result in
* @return {string} The JSON part of the sub structure
* @throws {string} If decoding could not be performed
* @private
*/
LabComm.Decoder.prototype.decodeSignature_ = function(name, json, fb, level, opt_parentArray) {
var type = this.data.getVarint();
switch(type) {
case LabComm.Constants.ARRAY:
var arrayVariable = 'a'+this.uid_++;
if(!opt_parentArray) {
//only add top-most array to final result
json.push('"'+name+'":'+arrayVariable);
}
//read number of indecies
var indecies = this.data.getVarint();
if(indecies === 0) {
throw "Array cant have 0 dimensions";
}
//get dimensions of the indecies and build for loop header
var dims = new Array(indecies);
var dimStr = new Array(indecies);
var dimVars = new Array(indecies);
for(var i = 0; i < indecies; i++) {
var dim = this.data.getVarint();
dims[i] = dim;
dimStr[i] = (dim === 0)? '_' : dim; // 0 means dynamic array
if(dim === 0) {
var arrayDimVar = 'd' + this.uid_++;
fb.push(this.indent_(level) + 'var ' + arrayDimVar + ' = data.getVarint();\n');
dim = arrayDimVar;
}
dimVars[i] = dim;
}
//create for loop(s)
var endFor = [],
nextVar, indexVar, prevVar,
currentVar = arrayVariable;
for(var i = 0; i < indecies; i++) {
indexVar = 'i' + this.uid_++;
fb.push(this.indent_(level+i) + 'var ' + currentVar + ' = new Array(' + dimVars[i] + ');\n');
fb.push(this.indent_(level+i) + 'for(var '+indexVar+' = 0; '+indexVar+' < ' + dimVars[i] + '; '+indexVar+'++) {\n')
var nextVar = 'a' + this.uid_++;
endFor.unshift(
this.indent_(level+i+1) + '}\n' +
this.indent_(level+i+1) + arrayVariable + '[' + indexVar + '] = ' + nextVar + ';\n' +
this.indent_(level+i) + '}\n'
);
prevVar = currentVar;
currentVar = nextVar;
}
endFor.shift(); // remove last
//propagate decoding
var arrayType = this.decodeSignature_(name, json, fb, level+indecies, prevVar+'['+indexVar+']');
//if we only have one index we need to explicitly end the foor loop
if(indecies == 1) {
endFor.unshift(this.indent_(level) + '}\n');
}
//create for loop footer
fb.push(endFor.join(''));
if(opt_parentArray) {
fb.push(this.indent_(level) + opt_parentArray+' = '+arrayVariable+';\n');
}
//if we have a parent array just return the type
if(opt_parentArray) {
return arrayType + '['+dimStr.join(', ')+']';
}
var arrayParts = this.exctractArray_(arrayType);
//we need to handle structs seperatly since they contains JSON object
if(arrayParts.type.charAt(0)=='{') {
return '"'+name+'['+dimStr.join(', ')+']'+arrayParts.dim+'":'+arrayParts.type;
}
return '"'+name+'['+dimStr.join(', ')+']'+arrayParts.dim+'":'+'"'+arrayParts.type+'"';
case LabComm.Constants.STRUCT:
var func = [];
var subJson = [];
var structVar = 's' + this.uid_++;
if(opt_parentArray) {
func.push('{'); //we get the name from the parent array
}
else {
func.push('"'+name+'":{');
}
var fields = this.data.getVarint();
//TODO: fields == 0 means a void type
while(fields--) {
var fn = this.data.getString();
func.push(this.decodeSignature_(fn, subJson, fb, level));
if(fields!=0) {
subJson.push(',');
func.push(",");
}
}
func.push("}");
if(opt_parentArray) {
fb.push(this.indent_(level) + opt_parentArray + ' = {' + subJson.join('') + '};\n');
}
else {
fb.push(this.indent_(level) + 'var ' + structVar + ' = {' + subJson.join('') + '};\n');
json.push('"'+name+'":' + structVar);
}
return func.join('');
case LabComm.Constants.BOOLEAN:
return this.buildDecoder_(name, json, fb, "bool", "data.getBoolean()", level, opt_parentArray);
case LabComm.Constants.BYTE:
return this.buildDecoder_(name, json, fb, "byte", "data.getByte()", level, opt_parentArray);
case LabComm.Constants.SHORT:
return this.buildDecoder_(name, json, fb, "short", "data.getShort()", level, opt_parentArray);
case LabComm.Constants.INT:
return this.buildDecoder_(name, json, fb, "int", "data.getInt()", level, opt_parentArray);
case LabComm.Constants.LONG:
return this.buildDecoder_(name, json, fb, "long", "data.getLong()", level, opt_parentArray);
case LabComm.Constants.FLOAT:
return this.buildDecoder_(name, json, fb, "float", "data.getFloat()", level, opt_parentArray);
case LabComm.Constants.DOUBLE:
return this.buildDecoder_(name, json, fb, "double", "data.getDouble()", level, opt_parentArray);
case LabComm.Constants.STRING:
return this.buildDecoder_(name, json, fb, "string", "data.getString()", level, opt_parentArray);
}
throw "Unknown type found in signature: 0x" + type.toString(16);
};
/**
* Decodes a buffer to Samples
* @param {LabComm.Buffer} buffer The buffer to decode
* @throws {string} If error during decoding
*/
LabComm.Decoder.prototype.decodePackage = function(buffer) {
this.data = buffer;
while(this.data.hasRemaning()) {
var tag = this.data.getVarint();
var len = this.data.getVarint();
//console.log('0x' + tag.toString(16) + ':' + len);
switch(tag) {
case LabComm.Constants.VERSION:
var version = this.data.getString();
if(version !== "LabComm2014") {
throw "Unsupported LabComm version: " + version;
}
break;
case LabComm.Constants.SAMPLE_DEF:
var index = this.data.getVarint();
var name = this.data.getString();
var siglen = this.data.getVarint();
var json = [],
fb = [];
var struct = JSON.parse('{' + this.decodeSignature_(name, json, fb, 1) + "}");
var func = fb.join('') + '\n\treturn {' + json.join('') + '};';
this.declarations_[index] = new LabComm.SampleDeclaration(index, name, struct, new Function('data', func));
break;
case LabComm.Constants.TYPE_DEF:
//TODO: same as sample_def
this.data.skip(len);
break;
case LabComm.Constants.TYPE_BINDING:
var sampleIndex = this.data.getVarint();
var typeIndex = this.data.getVarint();
//TODO: bind?
break;
case LabComm.Constants.SAMPLE_REF:
case LabComm.Constants.PRAGMA:
this.data.skip(len);
console.log("skipping unhandled package of size " + len);
break;
default:
var decl = this.declarations_[tag];
var sample = decl.decode(this.data);
this.callback_(decl, sample);
break;
}
}
};
/**
* A Sample declaration
* @constructor
* @param {number} index The samles tag-index
* @param {string} name The name of the sample
* @param {*} struct A JSON representation of the sample
* @param {function(LabComm.Buffer) : Object} func The decoder function
*/
LabComm.SampleDeclaration = function(index, name, struct, func) {
/**
* The tag index
* @type{number}
*/
this.index = index;
/**
* The name of the sample
* @type {string}
*/
this.name = name;
/**
* The JSON representation of the sample, can be used to re-create the .lc file
* @type {*}
*/
this.struct = struct;
/**
* The decoder that turns a buffer into a JSON sample
* @type {function(LabComm.Buffer) : Object}
* @private
*/
this.decoder_ = func;
};
/**
* Decodes a complete sample and returns a JSON obejct with the data
* @param {LabComm.Buffer} data The buffer that contains the sample
* @return {Object} The JSON object with the data
*/
LabComm.SampleDeclaration.prototype.decode = function(data) {
return this.decoder_(data);
};
'use strict';
(function() {
function decodeFromUrl(url) {
var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
oReq.responseType = "arraybuffer";
oReq.onload = function (oEvent) {
var arrayBuffer = oReq.response;
if (arrayBuffer) {
var buff = new LabComm.Buffer(new DataView(arrayBuffer));
window.decoder = new LabComm.Decoder(function(decl, data) {
testObject(decl, data);
});
window.decoder.decodePackage(buff);
}
};
oReq.send(null);
}
decodeFromUrl('http://fair-2014-4.lab1.cs.lth.se:8080/logdata/clamping/experiments/e12c4b31-5b32-41c0-b6a3-a5ad045aa997/data');
var referenceStruct ={
"log_message":{
"sequence":"int",
"message":"string",
"data":{
"f_value":"float",
"d_value":"double",
"l_value":"long",
"s_value":"short",
"b_value":"byte",
"theStack":{
"level":"int",
"name":"string",
"fatal":"bool"
}
},
"xyz[3]":"int",
"vect[_]":"int",
"fix_mat[3][_]":"int",
"dyn_mat[_][2]":"int",
"fix_mul[_, 4]":"int",
"dyn_mul[_, 2]":"int",
"fix_mulmat[1, _][_]":"int",
"fix_matmul[_][2, _]":"int",
"dyn_mulmat[_, _][3]":"int",
"dyn_matmul[1][_, 3]":"int",
"points[_]":{
"x":"float",
"y":"float"
}
}
};
var referenceObject = {
log_message: { //struct
sequence: 10, //int
message: 'Test', //string
data: { //struct
f_value: 1.6, //float
d_value: 1000000.0, //double
l_value: 145, //long
s_value: 10, //short
b_value: 0xA, //byte
theStack: { //struct
level: 2, //int
name: 'Robot.Move.toPos(10)', //string
fatal: true //bool
}
},
xyz: [1,2,3], //int[3]
vect: [8,7,6,5,4,3], //int[_]
fix_mat: [[1,2,3,4],[2,3,4,5],[3,4,5,6]], //int[3][_]
dyn_mat: [[1,2],[3,4]], //int[_][2]
fix_mul: [[1,2,3,4],[2,3,4,5],[3,4,5,6]], //int[_, 4]
dyn_mul: [[1,2],[3,4]], //int[_, 2]
fix_mulmat: [[[1,2,3],[4,5,6]]], //int[1, _][_]
fix_matmul: [[[1,2,3],[4,5,6]]], //int[_][2, _]
dyn_mulmat: [[[1,2,3],[4,5,6]]], //int[_, _][3]
dyn_matmul: [[[1,2,3],[4,5,6]]], //int[1][_, 3]
points: [
{x: 4.6, y: 2.3},
{x: 7.1, y: 1.0},
{x: 6.2, y: 5.9}
] //struct { float x; float y; } points[_]
}
};
function testObject(decl, obj) {
var i = 0;
console.log('-----Running tests-----');
testCase('Decode Sample Decalaration', referenceStruct, decl.struct) && i++;
testCase('Decode Data Sample', referenceObject, obj) && i++;
console.log('----------');
console.log(i + ' of 2 tests success');
console.log('----------');
}
function testCase(msg, r, t) {
var test = compareObjects(r, t);
console.log('Test "' + msg + '": ' + (test?'Success':'Fail'));
if(!test) {
console.log(t);
console.log('but expected: ');
console.log(r);
}
return test;
}
function compareObjects(o, p) {
var i,
keysO = Object.keys(o).sort(),
keysP = Object.keys(p).sort();
if (keysO.length !== keysP.length) {
return false;//not the same nr of keys
}
if (keysO.join('') !== keysP.join('')) {
return false;//different keys
}
for (i=0;i<keysO.length;++i) {
var x = o[keysO[i]],
y = p[keysO[i]];
if (x instanceof Array) {
if (!(y instanceof Array))
return false;
//if (compareObjects(o[keysO[i]], p[keysO[i]] === false) return false
//would work, too, and perhaps is a better fit, still, this is easy, too
if (y.join('') !== x.join(''))
return false;
}
else if (x instanceof Date) {
if (!(y instanceof Date))
return false;
if ((''+x) !== (''+y))
return false;
}
else if (x instanceof Function) {
if (!(y instanceof Function))
return false;
//ignore functions
}
if (x instanceof Object) {
if (!(y instanceof Object))
return false;
if (x === o) {//self reference?
if (y !== p)
return false;
}
else if (compareObjects(x, y) === false) {
return false;//WARNING: does not deal with circular refs other than ^^
}
}
else if(Number(x) === x) { //number
if((x|0) === x) { //int
if(x!==y) {
return false;
}
}
else { //float point, allow deviation
var diff = Math.abs(x-y);
if(diff > 1e-6) {
return false;
}
}
}
else if (x !== y) {
return false;
}
}
return true;
}
})();