422 lines
14 KiB
JavaScript
422 lines
14 KiB
JavaScript
define([
|
|
'jquery', 'knockout', 'text!./MassBalance.html', 'flot', 'kojqui/slider',
|
|
], function(jquery, ko, htmlString) {
|
|
|
|
ko.bindingHandlers.flotchart = {
|
|
init : function(element, valueAccessor, allBindings) {
|
|
// This will be called when the binding is first applied to an
|
|
// element
|
|
// Set up any initial state, event handlers, etc. here
|
|
var value = valueAccessor() || {};
|
|
|
|
var plot = jquery.plot(element, []);
|
|
ko.utils.domData.set(element, "flotchart-plot", plot);
|
|
|
|
if (value.hover && typeof (value.hover) === 'function') {
|
|
$(element).bind("plothover", function(event, pos, item) {
|
|
value.hover(pos, item);
|
|
});
|
|
}
|
|
|
|
if (ko.isObservable(value.options)) {
|
|
value.options.subscribe(function(newValue) {
|
|
var element = this;
|
|
// options changed - start with a new plot, reuse data
|
|
var plot = ko.utils.domData.get(element, "flotchart-plot");
|
|
plot = jquery.plot(element, plot.getData(), newValue);
|
|
ko.utils.domData.set(element, "flotchart-plot", plot);
|
|
}, element);
|
|
}
|
|
|
|
if (ko.isObservable(value.data)) {
|
|
value.data.subscribe(function(newValue) {
|
|
var element = this;
|
|
|
|
var plot = ko.utils.domData.get(element, "flotchart-plot");
|
|
plot.setData(newValue);
|
|
// TODO: setupGrid not always required
|
|
plot.setupGrid();
|
|
plot.draw();
|
|
|
|
}, element);
|
|
}
|
|
|
|
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
|
|
// This will be called when the element is removed by Knockout
|
|
// or
|
|
// if some other part of your code calls ko.removeNode(element)
|
|
var plot = ko.utils.domData.set(element, "flotchart-plot", null);
|
|
// TODO: unsubscribe from data and options observables?
|
|
});
|
|
|
|
},
|
|
|
|
};
|
|
|
|
function ViewModel(params) {
|
|
var self = this;
|
|
|
|
var Series = {
|
|
ENVELOPE : 0,
|
|
PAYLOAD : 1,
|
|
FUEL : 2,
|
|
CG : 3
|
|
}
|
|
|
|
self.envelopeData = ko.observableArray([
|
|
// Series 0: Envelope
|
|
{
|
|
color : 'rgb(192, 128, 0)',
|
|
data : [],
|
|
label : "Envelope",
|
|
lines : {
|
|
show : true
|
|
},
|
|
points : {
|
|
show : false
|
|
},
|
|
bars : {
|
|
show : false
|
|
},
|
|
shadowSize : 0,
|
|
|
|
},
|
|
// Series 1: Payload
|
|
{
|
|
color : 'rgb(0, 0, 255)',
|
|
data : [],
|
|
label : "Payload",
|
|
lines : {
|
|
show : true
|
|
},
|
|
points : {
|
|
show : true
|
|
},
|
|
bars : {
|
|
show : false
|
|
},
|
|
|
|
},
|
|
// Series 2: Fuel
|
|
{
|
|
color : 'rgb(0, 255, 0)',
|
|
data : [],
|
|
label : "Fuel",
|
|
lines : {
|
|
show : true
|
|
},
|
|
points : {
|
|
show : true
|
|
},
|
|
bars : {
|
|
show : false
|
|
},
|
|
|
|
},
|
|
// Series 3: CG
|
|
{
|
|
color : 'rgb(255, 0, 0)',
|
|
data : [],
|
|
label : "CG",
|
|
lines : {
|
|
show : false
|
|
},
|
|
points : {
|
|
show : true
|
|
},
|
|
bars : {
|
|
show : false
|
|
},
|
|
|
|
},
|
|
]);
|
|
|
|
self.hover = function(pos, item) {
|
|
if( ! item ) {
|
|
self.hoverLabel("");
|
|
self.hoverMass(0);
|
|
self.hoverCG(0);
|
|
return;
|
|
}
|
|
|
|
switch (item.seriesIndex) {
|
|
case Series.CG:
|
|
self.hoverLabel("CG");
|
|
break;
|
|
case Series.PAYLOAD:
|
|
self.hoverLabel("Load"); //self.loads()[item.dataIndex].name;
|
|
break;
|
|
|
|
case Series.FUEL:
|
|
self.hoverLabel("Fuel"); //self.tanks()[item.dataIndex].name;
|
|
break;
|
|
|
|
}
|
|
self.hoverCG(item.datapoint[0]);
|
|
self.hoverMass(item.datapoint[1]);
|
|
|
|
}
|
|
|
|
self.hoverLabel = ko.observable();
|
|
self.hoverMass = ko.observable(0);
|
|
self.hoverCG = ko.observable(0);
|
|
|
|
self.envelopeOptions = ko.observable({
|
|
legend : {
|
|
show : false,
|
|
},
|
|
grid : {
|
|
hoverable : true,
|
|
}
|
|
});
|
|
|
|
self.tanks = ko.observableArray([]);
|
|
self.loads = ko.observableArray([]);
|
|
self.cglimits = ko.observableArray([]);
|
|
|
|
self.weight = ko.observable(0).extend({
|
|
fgprop : 'weight'
|
|
});
|
|
self.cg = ko.observable(0).extend({
|
|
fgprop : 'cg'
|
|
});
|
|
|
|
self.currentCG = ko.computed(function() {
|
|
return [
|
|
self.cg(), self.weight()
|
|
];
|
|
}).extend({
|
|
rateLimit : 1000
|
|
});
|
|
|
|
self.currentCG.subscribe(function(newValue) {
|
|
var data = self.envelopeData();
|
|
|
|
var p = newValue;
|
|
|
|
data[Series.CG].data = [
|
|
p
|
|
];
|
|
|
|
var fuelSeriesData = [
|
|
p
|
|
];
|
|
var moment = newValue[0] * newValue[1];
|
|
var mass = newValue[1];
|
|
self.tanks().forEach(function(tank) {
|
|
moment -= tank.moment();
|
|
mass -= tank.mass();
|
|
p = [
|
|
moment / mass, mass
|
|
];
|
|
fuelSeriesData.push(p);
|
|
});
|
|
data[Series.FUEL].data = fuelSeriesData;
|
|
|
|
var payloadSeriesData = [
|
|
p
|
|
];
|
|
|
|
self.loads().forEach(function(load) {
|
|
moment -= load.moment();
|
|
mass -= load.mass();
|
|
p = [
|
|
moment / mass, mass
|
|
];
|
|
payloadSeriesData.push(p);
|
|
});
|
|
data[Series.PAYLOAD].data = payloadSeriesData;
|
|
|
|
self.envelopeData(data);
|
|
|
|
});
|
|
|
|
self.cglimits.subscribe(function(newValue) {
|
|
var bounds = {
|
|
xMin : Number.MAX_VALUE,
|
|
xMax : Number.MIN_VALUE,
|
|
yMin : Number.MAX_VALUE,
|
|
yMax : Number.MIN_VALUE,
|
|
};
|
|
var envelope = [];
|
|
|
|
self.cglimits().forEach(function(entry) {
|
|
envelope.push([
|
|
entry.position, entry.mass
|
|
]);
|
|
if (entry.mass < this.yMin)
|
|
this.yMin = entry.mass;
|
|
if (entry.mass > this.yMax)
|
|
this.yMax = entry.mass;
|
|
if (entry.position < this.xMin)
|
|
this.xMin = entry.position;
|
|
if (entry.position > this.xMax)
|
|
this.xMax = entry.position;
|
|
}, bounds);
|
|
if (envelope.length > 0)
|
|
envelope.push(envelope[0]);
|
|
|
|
var options = self.envelopeOptions() || {};
|
|
options.xaxis = options.xaxis || {};
|
|
options.yaxis = options.yaxis || {};
|
|
options.xaxis.min = bounds.xMin;
|
|
options.xaxis.max = bounds.xMax;
|
|
options.yaxis.min = bounds.yMin;
|
|
options.yaxis.max = bounds.yMax;
|
|
self.envelopeOptions(options);
|
|
var ed = self.envelopeData();
|
|
ed[0].data = envelope;
|
|
self.envelopeData(ed);
|
|
});
|
|
|
|
var WeightViewModel = function(number) {
|
|
var self = this;
|
|
|
|
self.number = number;
|
|
self.name = 'Load' + number.toString();
|
|
self.arm = ko.observable(1);
|
|
self.mass = ko.observable(0);
|
|
self.min = 0;
|
|
self.max = 0;
|
|
|
|
self.moment = ko.pureComputed(function() {
|
|
return self.mass() * self.arm();
|
|
});
|
|
|
|
self.setLoad = function() {
|
|
jquery.post('/json/payload/weight[' + this.number + ']', JSON.stringify({
|
|
name : 'weight-lb',
|
|
value : this.mass()
|
|
}));
|
|
}
|
|
}
|
|
|
|
jquery.get('/json/payload?d=2', null, function(data) {
|
|
|
|
var assemble = function(data) {
|
|
|
|
var loads = [];
|
|
data.children.forEach(function(prop) {
|
|
if (prop.name === 'weight') {
|
|
var weight = new WeightViewModel(loads.length);
|
|
loads.push(weight);
|
|
prop.children.forEach(function(prop) {
|
|
if (prop.name === 'name') {
|
|
weight.name = prop.value;
|
|
} else if (prop.name === 'weight-lb') {
|
|
weight.mass(Number(prop.value));
|
|
} else if (prop.name == 'min-lb') {
|
|
weight.min = Number(prop.value);
|
|
} else if (prop.name == 'max-lb') {
|
|
weight.max = Number(prop.value);
|
|
} else if (prop.name == 'arm-in') {
|
|
weight.arm(Number(prop.value));
|
|
}
|
|
});
|
|
}
|
|
});
|
|
return loads;
|
|
|
|
}
|
|
|
|
self.loads(assemble(data));
|
|
});
|
|
|
|
jquery.get('/json/limits/mass-and-balance/cg/limit?d=2', null, function(data) {
|
|
|
|
var assemble = function(data) {
|
|
|
|
var cgLimits = [];
|
|
data.children.forEach(function(prop) {
|
|
if (prop.name === 'entry') {
|
|
var entry = {
|
|
position : 0,
|
|
mass : 0
|
|
};
|
|
cgLimits.push(entry);
|
|
prop.children.forEach(function(prop) {
|
|
if (prop.name === 'position') {
|
|
entry.position = Number(prop.value);
|
|
} else if (prop.name == 'mass-lbs') {
|
|
entry.mass = Number(prop.value);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
return cgLimits;
|
|
|
|
}
|
|
|
|
self.cglimits(assemble(data));
|
|
});
|
|
|
|
var TankViewModel = function(number) {
|
|
var self = this;
|
|
|
|
self.name = 'Tank' + number.toString();
|
|
self.number = number;
|
|
self.capacity = 0;
|
|
self.content = ko.observable(0);
|
|
self.arm = ko.observable(1);
|
|
self.density = ko.observable(0);
|
|
self.hidden = false;
|
|
self.mass = ko.pureComputed(function() {
|
|
return self.content() * self.density();
|
|
});
|
|
|
|
self.moment = ko.pureComputed(function() {
|
|
return self.mass() * self.arm();
|
|
});
|
|
|
|
self.setTankLevel = function() {
|
|
jquery.post('/json/consumables/fuel/tank[' + this.number + ']', JSON.stringify({
|
|
name : 'level-gal_us',
|
|
value : this.content()
|
|
}));
|
|
}
|
|
}
|
|
|
|
jquery.get('/json/consumables/fuel?d=2', null, function(data) {
|
|
|
|
var assemble = function(data) {
|
|
|
|
var tanks = [];
|
|
data.children.forEach(function(prop) {
|
|
if (prop.name === 'tank') {
|
|
var tank = new TankViewModel(tanks.length);
|
|
tanks.push(tank);
|
|
prop.children.forEach(function(prop) {
|
|
if (prop.name === 'name') {
|
|
tank.name = prop.value;
|
|
} else if (prop.name == 'capacity-gal_us') {
|
|
tank.capacity = Number(prop.value);
|
|
} else if (prop.name == 'density-ppg') {
|
|
tank.density(Number(prop.value));
|
|
} else if (prop.name == 'level-gal_us') {
|
|
tank.content(Number(prop.value));
|
|
} else if (prop.name == 'arm-in') {
|
|
tank.arm(Number(prop.value));
|
|
} else if (prop.name == 'hidden') {
|
|
tank.hidden = prop.value !== 'false';
|
|
}
|
|
});
|
|
}
|
|
});
|
|
return tanks;
|
|
|
|
}
|
|
|
|
self.tanks(assemble(data));
|
|
|
|
});
|
|
}
|
|
|
|
ViewModel.prototype.dispose = function() {
|
|
}
|
|
|
|
// Return component definition
|
|
return {
|
|
viewModel : ViewModel,
|
|
template : htmlString
|
|
};
|
|
});
|