2018-12-12 04:04:00 +00:00
|
|
|
"use strict";
|
|
|
|
var connection = null;
|
|
|
|
|
|
|
|
var WebSocket = WebSocket || MozWebSocket;
|
|
|
|
|
|
|
|
// Implement bootstrap 3 style button loading support
|
|
|
|
(function($) {
|
|
|
|
$.fn.button = function(action) {
|
|
|
|
if (action === 'loading' && this.data('loading-text')) {
|
|
|
|
this.data('original-text', this.html()).html(this.data('loading-text')).prop('disabled', true);
|
2018-12-20 21:26:14 +00:00
|
|
|
feather.replace();
|
2018-12-12 04:04:00 +00:00
|
|
|
}
|
|
|
|
if (action === 'reset' && this.data('original-text')) {
|
|
|
|
this.html(this.data('original-text')).prop('disabled', false);
|
2018-12-20 21:26:14 +00:00
|
|
|
feather.replace();
|
2018-12-12 04:04:00 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}(jQuery));
|
|
|
|
|
|
|
|
// HTML escaping
|
|
|
|
var entityMap = {
|
|
|
|
'&': '&',
|
|
|
|
'<': '<',
|
|
|
|
'>': '>',
|
|
|
|
'"': '"',
|
|
|
|
"'": ''',
|
|
|
|
'/': '/',
|
|
|
|
'`': '`',
|
|
|
|
'=': '='
|
|
|
|
};
|
|
|
|
|
|
|
|
function escapeHtml(string) {
|
|
|
|
return String(string).replace(/[&<>"'`=\/]/g, function (s) {
|
|
|
|
return entityMap[s];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function displayStatus(message) {
|
|
|
|
$('#status-content').html('<div id="status" class="alert alert-warning alert-dismissable fade show" role="alert"><span>' + escapeHtml(message) + '</span><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button></div>');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable and disable buttons based on connection status
|
2019-01-13 23:44:59 +00:00
|
|
|
var connectedButtonIds = ['systemRestart', 'networkApproach', 'networkAddress', 'networkMask', 'networkGateway', 'networkDNS', 'visionUp', 'visionDown', 'visionTerm', 'visionKill', 'systemReadOnly', 'systemWritable', 'visionClient', 'visionTeam', 'visionDiscard', 'addConnectedCamera', 'addCamera', 'applicationType'];
|
2019-01-14 00:59:08 +00:00
|
|
|
var connectedButtonClasses = ['cameraName', 'cameraPath', 'cameraAlternatePaths', 'cameraPixelFormat', 'cameraWidth', 'cameraHeight', 'cameraFps', 'cameraBrightness', 'cameraWhiteBalance', 'cameraExposure', 'cameraProperties', 'streamWidth', 'streamHeight', 'streamFps', 'streamCompression', 'streamDefaultCompression', 'cameraRemove', 'cameraCopyConfig']
|
2018-12-20 21:26:14 +00:00
|
|
|
var writableButtonIds = ['networkSave', 'visionSave', 'applicationSave'];
|
2018-12-12 04:04:00 +00:00
|
|
|
var systemStatusIds = ['systemMemoryFree1s', 'systemMemoryFree5s',
|
|
|
|
'systemMemoryAvail1s', 'systemMemoryAvail5s',
|
|
|
|
'systemCpuUser1s', 'systemCpuUser5s',
|
|
|
|
'systemCpuSystem1s', 'systemCpuSystem5s',
|
|
|
|
'systemCpuIdle1s', 'systemCpuIdle5s',
|
|
|
|
'systemNetwork1s', 'systemNetwork5s'];
|
|
|
|
|
|
|
|
function displayDisconnected() {
|
|
|
|
displayReadOnly();
|
|
|
|
$('#connectionBadge').removeClass('badge-primary').addClass('badge-secondary').text('Disconnected');
|
|
|
|
$('#visionServiceStatus').removeClass('badge-primary').removeClass('badge-secondary').addClass('badge-dark').text('Unknown Status');
|
2019-01-13 23:44:59 +00:00
|
|
|
$('.cameraConnectionBadge').removeClass('badge-primary').removeClass('badge-secondary').addClass('badge-dark').text('Unknown Status');
|
2018-12-12 04:04:00 +00:00
|
|
|
for (var i = 0; i < connectedButtonIds.length; i++) {
|
|
|
|
$('#' + connectedButtonIds[i]).prop('disabled', true);
|
|
|
|
}
|
2018-12-20 21:26:14 +00:00
|
|
|
for (var i = 0; i < connectedButtonClasses.length; i++) {
|
|
|
|
$('.' + connectedButtonClasses[i]).prop('disabled', true);
|
|
|
|
}
|
2018-12-12 04:04:00 +00:00
|
|
|
for (var i = 0; i < systemStatusIds.length; i++) {
|
|
|
|
$('#' + systemStatusIds[i]).text("");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function displayConnected() {
|
|
|
|
$('#connectionBadge').removeClass('badge-secondary').addClass('badge-primary').text('Connected');
|
|
|
|
for (var i = 0; i < connectedButtonIds.length; i++) {
|
|
|
|
$('#' + connectedButtonIds[i]).prop('disabled', false);
|
|
|
|
}
|
2018-12-20 21:26:14 +00:00
|
|
|
for (var i = 0; i < connectedButtonClasses.length; i++) {
|
|
|
|
$('.' + connectedButtonClasses[i]).prop('disabled', false);
|
|
|
|
}
|
2018-12-12 04:04:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Enable and disable buttons based on writable status
|
|
|
|
function displayReadOnly() {
|
|
|
|
for (var i = 0; i < writableButtonIds.length; i++) {
|
|
|
|
$('#' + writableButtonIds[i]).prop('disabled', true);
|
|
|
|
}
|
|
|
|
$('#systemReadOnly').addClass('active').prop('aria-pressed', true);
|
|
|
|
$('#systemWritable').removeClass('active').prop('aria-pressed', false);
|
|
|
|
}
|
|
|
|
|
|
|
|
function displayWritable() {
|
|
|
|
for (var i = 0; i < writableButtonIds.length; i++) {
|
|
|
|
$('#' + writableButtonIds[i]).prop('disabled', false);
|
|
|
|
}
|
|
|
|
$('#systemReadOnly').removeClass('active').prop('aria-pressed', false);
|
|
|
|
$('#systemWritable').addClass('active').prop('aria-pressed', true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle Read-Only and Writable buttons
|
|
|
|
$('#systemReadOnly').click(function() {
|
|
|
|
var $this = $(this);
|
|
|
|
if ($this.hasClass('active')) return;
|
|
|
|
var msg = {
|
|
|
|
type: 'systemReadOnly'
|
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#systemWritable').click(function() {
|
|
|
|
var $this = $(this);
|
|
|
|
if ($this.hasClass('active')) return;
|
|
|
|
var msg = {
|
|
|
|
type: 'systemWritable'
|
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
});
|
|
|
|
|
2018-12-20 21:26:14 +00:00
|
|
|
// Vision settings
|
|
|
|
var visionSettingsServer = {};
|
|
|
|
var visionSettingsDisplay = {'cameras': []};
|
2019-01-13 23:44:59 +00:00
|
|
|
var cameraList = [];
|
2018-12-20 21:26:14 +00:00
|
|
|
|
2018-12-29 04:48:07 +00:00
|
|
|
function pushVisionLogEnabled() {
|
|
|
|
var msg = {
|
|
|
|
type: 'visionLogEnabled',
|
|
|
|
value: visionLogEnabled.prop('checked')
|
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
}
|
|
|
|
|
2018-12-12 04:04:00 +00:00
|
|
|
// WebSocket automatic reconnection timer
|
|
|
|
var reconnectTimerId = 0;
|
|
|
|
|
|
|
|
// Establish WebSocket connection
|
|
|
|
function connect() {
|
|
|
|
if (connection && connection.readyState !== WebSocket.CLOSED) return;
|
|
|
|
var serverUrl = "ws://" + window.location.hostname;
|
|
|
|
if (window.location.port !== '') {
|
|
|
|
serverUrl += ':' + window.location.port;
|
|
|
|
}
|
|
|
|
connection = new WebSocket(serverUrl, 'frcvision');
|
|
|
|
connection.onopen = function(evt) {
|
|
|
|
if (reconnectTimerId) {
|
|
|
|
window.clearInterval(reconnectTimerId);
|
|
|
|
reconnectTimerId = 0;
|
|
|
|
}
|
|
|
|
displayConnected();
|
2018-12-29 04:48:07 +00:00
|
|
|
pushVisionLogEnabled();
|
2018-12-12 04:04:00 +00:00
|
|
|
};
|
|
|
|
connection.onclose = function(evt) {
|
|
|
|
displayDisconnected();
|
|
|
|
if (!reconnectTimerId) {
|
|
|
|
reconnectTimerId = setInterval(function() { connect(); }, 2000);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// WebSocket incoming message handling
|
|
|
|
connection.onmessage = function(evt) {
|
|
|
|
var msg = JSON.parse(evt.data);
|
2018-12-20 21:26:14 +00:00
|
|
|
if (msg === null) {
|
|
|
|
return;
|
|
|
|
}
|
2018-12-12 04:04:00 +00:00
|
|
|
switch (msg.type) {
|
|
|
|
case 'systemStatus':
|
|
|
|
for (var i = 0; i < systemStatusIds.length; i++) {
|
|
|
|
$('#' + systemStatusIds[i]).text(msg[systemStatusIds[i]]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'visionStatus':
|
|
|
|
var elem = $('#visionServiceStatus');
|
|
|
|
if (msg.visionServiceStatus) {
|
|
|
|
elem.text(msg.visionServiceStatus);
|
|
|
|
}
|
|
|
|
if (msg.visionServiceEnabled && !elem.hasClass('badge-primary')) {
|
|
|
|
elem.removeClass('badge-dark').removeClass('badge-secondary').addClass('badge-primary');
|
|
|
|
} else if (!msg.visionServiceEnabled && !elem.hasClass('badge-secondary')) {
|
|
|
|
elem.removeClass('badge-dark').removeClass('badge-primary').addClass('badge-secondary');
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'visionLog':
|
|
|
|
visionLog(msg.data);
|
|
|
|
break;
|
|
|
|
case 'networkSettings':
|
2018-12-17 08:54:25 +00:00
|
|
|
$('#networkApproach').val(msg.networkApproach);
|
|
|
|
$('#networkAddress').val(msg.networkAddress);
|
|
|
|
$('#networkMask').val(msg.networkMask);
|
|
|
|
$('#networkGateway').val(msg.networkGateway);
|
|
|
|
$('#networkDNS').val(msg.networkDNS);
|
2018-12-17 10:15:11 +00:00
|
|
|
updateNetworkSettingsView();
|
2018-12-12 04:04:00 +00:00
|
|
|
break;
|
2018-12-20 21:26:14 +00:00
|
|
|
case 'visionSettings':
|
|
|
|
visionSettingsServer = msg.settings;
|
|
|
|
visionSettingsDisplay = $.extend(true, {}, visionSettingsServer);
|
|
|
|
updateVisionSettingsView();
|
|
|
|
break;
|
|
|
|
case 'applicationSettings':
|
|
|
|
$('#applicationType').val(msg.applicationType);
|
|
|
|
updateApplicationView();
|
|
|
|
break;
|
|
|
|
case 'applicationSaveComplete':
|
|
|
|
$('#applicationSave').button('reset');
|
|
|
|
updateApplicationView();
|
|
|
|
break;
|
2018-12-12 04:04:00 +00:00
|
|
|
case 'systemReadOnly':
|
|
|
|
displayReadOnly();
|
|
|
|
break;
|
|
|
|
case 'systemWritable':
|
|
|
|
displayWritable();
|
|
|
|
break;
|
|
|
|
case 'status':
|
|
|
|
displayStatus(msg.message);
|
|
|
|
break;
|
2019-01-13 23:44:59 +00:00
|
|
|
case 'cameraList':
|
|
|
|
cameraList = msg.cameras;
|
|
|
|
updateCameraListView();
|
|
|
|
break;
|
2018-12-12 04:04:00 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Button handlers
|
|
|
|
$('#systemRestart').click(function() {
|
|
|
|
var msg = {
|
|
|
|
type: 'systemRestart'
|
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#visionUp').click(function() {
|
|
|
|
var msg = {
|
|
|
|
type: 'visionUp'
|
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#visionDown').click(function() {
|
|
|
|
var msg = {
|
|
|
|
type: 'visionDown'
|
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#visionTerm').click(function() {
|
|
|
|
var msg = {
|
|
|
|
type: 'visionTerm'
|
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#visionKill').click(function() {
|
|
|
|
var msg = {
|
|
|
|
type: 'visionKill'
|
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#visionLogEnabled').change(function() {
|
2018-12-29 04:48:07 +00:00
|
|
|
pushVisionLogEnabled();
|
2018-12-12 04:04:00 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
//
|
|
|
|
// Vision console output
|
|
|
|
//
|
|
|
|
var visionConsole = document.getElementById('visionConsole');
|
|
|
|
var visionLogEnabled = $('#visionLogEnabled');
|
|
|
|
var _linesLimit = 100;
|
|
|
|
|
|
|
|
/*
|
|
|
|
function escape_for_html(txt) {
|
|
|
|
return txt.replace(/[&<>]/gm, function(str) {
|
|
|
|
if (str == "&") return "&";
|
|
|
|
if (str == "<") return "<";
|
|
|
|
if (str == ">") return ">";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
function visionLog(data) {
|
|
|
|
if (!visionLogEnabled.prop('checked')) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var wasScrolledBottom = (visionConsole.scrollTop === (visionConsole.scrollHeight - visionConsole.offsetHeight));
|
|
|
|
var div = document.createElement('div');
|
|
|
|
var p = document.createElement('p');
|
|
|
|
p.className = 'inner-line';
|
|
|
|
|
|
|
|
// escape HTML tags
|
|
|
|
data = escapeHtml(data);
|
|
|
|
p.innerHTML = data;
|
|
|
|
|
|
|
|
div.className = 'line';
|
|
|
|
div.addEventListener('click', function click() {
|
|
|
|
if (this.className.indexOf('selected') === -1) {
|
|
|
|
this.className = 'line-selected';
|
|
|
|
} else {
|
|
|
|
this.className = 'line';
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
div.appendChild(p);
|
|
|
|
visionConsole.appendChild(div);
|
|
|
|
|
|
|
|
if (visionConsole.children.length > _linesLimit) {
|
|
|
|
visionConsole.removeChild(visionConsole.children[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wasScrolledBottom) {
|
|
|
|
visionConsole.scrollTop = visionConsole.scrollHeight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Show details when appropriate for network approach
|
2018-12-17 08:54:25 +00:00
|
|
|
function updateNetworkSettingsView() {
|
|
|
|
if ($('#networkApproach').val() === "dhcp") {
|
2018-12-12 04:04:00 +00:00
|
|
|
$('#networkIpDetails').collapse('hide');
|
|
|
|
} else {
|
|
|
|
$('#networkIpDetails').collapse('show');
|
|
|
|
}
|
2018-12-17 08:54:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$('#networkApproach').change(function() {
|
|
|
|
updateNetworkSettingsView();
|
2018-12-12 04:04:00 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Network Save button handler
|
|
|
|
$('#networkSave').click(function() {
|
|
|
|
var msg = {
|
|
|
|
type: 'networkSave',
|
2018-12-17 08:54:25 +00:00
|
|
|
networkApproach: $('#networkApproach').val(),
|
|
|
|
networkAddress: $('#networkAddress').val(),
|
|
|
|
networkMask: $('#networkMask').val(),
|
|
|
|
networkGateway: $('#networkGateway').val(),
|
|
|
|
networkDNS: $('#networkDNS').val()
|
2018-12-12 04:04:00 +00:00
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
});
|
|
|
|
|
|
|
|
// Show details when appropriate for NT client
|
|
|
|
$('#visionClient').change(function() {
|
|
|
|
if (this.checked) {
|
|
|
|
$('#visionClientDetails').collapse('show');
|
|
|
|
} else {
|
|
|
|
$('#visionClientDetails').collapse('hide');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-01-14 00:59:08 +00:00
|
|
|
function getCameraPropertyValue(data, name) {
|
|
|
|
if (data === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
for (var i = 0; i < data.length; i++) {
|
|
|
|
if (data[i].name === name) {
|
|
|
|
return data[i].value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2018-12-20 21:26:14 +00:00
|
|
|
function updateVisionCameraView(camera, value) {
|
|
|
|
if ('name' in value) {
|
|
|
|
camera.find('.cameraTitle').text('Camera ' + value.name);
|
|
|
|
camera.find('.cameraName').val(value.name);
|
|
|
|
}
|
|
|
|
if ('path' in value) {
|
|
|
|
camera.find('.cameraPath').val(value.path);
|
|
|
|
}
|
|
|
|
camera.find('.cameraPixelFormat').val(value['pixel format']);
|
|
|
|
camera.find('.cameraWidth').val(value.width);
|
|
|
|
camera.find('.cameraHeight').val(value.height);
|
|
|
|
camera.find('.cameraFps').val(value.fps);
|
|
|
|
camera.find('.cameraBrightness').val(value.brightness);
|
|
|
|
camera.find('.cameraWhiteBalance').val(value['white balance']);
|
|
|
|
camera.find('.cameraExposure').val(value.exposure);
|
|
|
|
camera.find('.cameraProperties').val(JSON.stringify(value.properties));
|
2019-01-14 00:59:08 +00:00
|
|
|
if ('stream' in value && 'properties' in value.stream) {
|
|
|
|
camera.find('.streamWidth').val(getCameraPropertyValue(value.stream.properties, 'width'));
|
|
|
|
camera.find('.streamHeight').val(getCameraPropertyValue(value.stream.properties, 'height'));
|
|
|
|
camera.find('.streamFps').val(getCameraPropertyValue(value.stream.properties, 'fps'));
|
|
|
|
camera.find('.streamCompression').val(getCameraPropertyValue(value.stream.properties, 'compression'));
|
|
|
|
camera.find('.streamDefaultCompression').val(getCameraPropertyValue(value.stream.properties, 'default_compression'));
|
|
|
|
} else {
|
|
|
|
camera.find('.streamWidth').val('');
|
|
|
|
camera.find('.streamHeight').val('');
|
|
|
|
camera.find('.streamFps').val('');
|
|
|
|
camera.find('.streamCompression').val('');
|
|
|
|
camera.find('.streamDefaultCompression').val('');
|
|
|
|
}
|
2018-12-20 21:26:14 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 06:31:55 +00:00
|
|
|
function updateVisionCameraDataFromJson(i, data) {
|
|
|
|
if (!('name' in data)) {
|
|
|
|
data.name = visionSettingsDisplay.cameras[i].name;
|
|
|
|
}
|
|
|
|
if (!('path' in data)) {
|
|
|
|
data.path = visionSettingsDisplay.cameras[i].path;
|
|
|
|
}
|
|
|
|
if ('properties' in data) {
|
|
|
|
var newProps = [];
|
|
|
|
var wbAuto = false;
|
|
|
|
var exAuto = false;
|
|
|
|
|
2019-01-03 23:58:19 +00:00
|
|
|
for (var j = 0; j < data.properties.length; j++) {
|
|
|
|
var name = data.properties[j].name;
|
2019-01-03 06:31:55 +00:00
|
|
|
|
|
|
|
// remove all raw properties
|
|
|
|
if (name.startsWith('raw_')) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// brightness
|
|
|
|
if (name === 'brightness') {
|
2019-01-03 23:58:19 +00:00
|
|
|
data.brightness = data.properties[j].value;
|
2019-01-03 06:31:55 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// white balance
|
|
|
|
if (name === 'white_balance_temperature_auto') {
|
2019-01-03 23:58:19 +00:00
|
|
|
if (data.properties[j].value === true) {
|
2019-01-03 06:31:55 +00:00
|
|
|
data['white balance'] = 'auto';
|
|
|
|
wbAuto = true;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (name === 'white_balance_temperature') {
|
|
|
|
if (wbAuto === false) {
|
|
|
|
data['white balance'] = data.properties.white_balance_temperature;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// exposure
|
|
|
|
if (name === 'exposure_auto') {
|
2019-01-03 23:58:19 +00:00
|
|
|
if (data.properties[j].value === 3) {
|
2019-01-03 06:31:55 +00:00
|
|
|
data.exposure = 'auto';
|
|
|
|
exAuto = true;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (name === 'exposure_absolute') {
|
|
|
|
if (exAuto === false) {
|
|
|
|
data.exposure = data.properties.exposure_absolute;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2019-01-03 23:58:19 +00:00
|
|
|
newProps.push(data.properties[j]);
|
2019-01-03 06:31:55 +00:00
|
|
|
}
|
|
|
|
data.properties = newProps;
|
|
|
|
}
|
|
|
|
visionSettingsDisplay.cameras[i] = data;
|
|
|
|
}
|
|
|
|
|
2018-12-20 21:26:14 +00:00
|
|
|
function appendNewVisionCameraView(value, i) {
|
|
|
|
var camera = $('#cameraNEW').clone();
|
|
|
|
camera.attr('id', 'camera' + i);
|
|
|
|
camera.addClass('cameraSetting');
|
|
|
|
camera.removeAttr('style');
|
|
|
|
|
|
|
|
updateVisionCameraView(camera, value);
|
|
|
|
camera.find('.cameraStream').attr('href', 'http://' + window.location.hostname + ':' + (1181 + i) + '/');
|
|
|
|
camera.find('.cameraRemove').click(function() {
|
|
|
|
visionSettingsDisplay.cameras.splice(i, 1);
|
|
|
|
camera.remove();
|
2019-01-13 23:44:59 +00:00
|
|
|
updateCameraListView();
|
2018-12-20 21:26:14 +00:00
|
|
|
});
|
|
|
|
camera.find('.cameraSettingsFile').change(function() {
|
|
|
|
if (this.files.length <= 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
var fr = new FileReader();
|
|
|
|
fr.onload = function(e) {
|
|
|
|
var result = JSON.parse(e.target.result);
|
2019-01-03 06:31:55 +00:00
|
|
|
updateVisionCameraDataFromJson(i, result);
|
2018-12-20 21:26:14 +00:00
|
|
|
updateVisionCameraView(camera, result);
|
|
|
|
};
|
|
|
|
fr.readAsText(this.files.item(0));
|
|
|
|
});
|
2019-01-03 06:31:55 +00:00
|
|
|
camera.find('.cameraCopyConfig').click(function() {
|
|
|
|
fetch('http://' + window.location.hostname + ':' + (1181 + i) + '/config.json')
|
|
|
|
.then(response => response.json())
|
|
|
|
.then(function(result) {
|
|
|
|
updateVisionCameraDataFromJson(i, result);
|
|
|
|
updateVisionCameraView(camera, result);
|
|
|
|
})
|
|
|
|
.catch(function(error) {
|
|
|
|
displayStatus('error reading camera config: ' + error);
|
|
|
|
});
|
|
|
|
});
|
2018-12-20 21:26:14 +00:00
|
|
|
|
|
|
|
camera.find('[id]').each(function() {
|
|
|
|
$(this).attr('id', $(this).attr('id').replace('NEW', i));
|
|
|
|
});
|
|
|
|
camera.find('[for]').each(function() {
|
|
|
|
$(this).attr('for', $(this).attr('for').replace('NEW', i));
|
|
|
|
});
|
|
|
|
camera.find('[data-target]').each(function() {
|
|
|
|
$(this).attr('data-target', $(this).attr('data-target').replace('NEW', i));
|
|
|
|
});
|
2019-01-13 23:44:59 +00:00
|
|
|
camera.find('[aria-labelledby]').each(function() {
|
|
|
|
$(this).attr('aria-labelledby', $(this).attr('aria-labelledby').replace('NEW', i));
|
|
|
|
});
|
2018-12-12 04:04:00 +00:00
|
|
|
|
2018-12-20 21:26:14 +00:00
|
|
|
$('#cameras').append(camera);
|
2018-12-12 04:04:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function updateVisionSettingsView() {
|
2018-12-20 21:26:14 +00:00
|
|
|
var isClient = !visionSettingsDisplay.ntmode || visionSettingsDisplay.ntmode === 'client';
|
|
|
|
$('#visionClient').prop('checked', isClient);
|
|
|
|
if (isClient) {
|
2018-12-12 04:04:00 +00:00
|
|
|
$('#visionClientDetails').collapse('show');
|
|
|
|
} else {
|
|
|
|
$('#visionClientDetails').collapse('hide');
|
|
|
|
}
|
2018-12-20 21:26:14 +00:00
|
|
|
$('#visionTeam').val(visionSettingsDisplay.team);
|
2018-12-12 04:04:00 +00:00
|
|
|
|
2018-12-20 21:26:14 +00:00
|
|
|
$('.cameraSetting').remove();
|
|
|
|
visionSettingsDisplay.cameras.forEach(function (value, i) {
|
|
|
|
appendNewVisionCameraView(value, i);
|
2018-12-12 04:04:00 +00:00
|
|
|
});
|
2019-01-13 23:44:59 +00:00
|
|
|
updateCameraListView();
|
2018-12-20 21:26:14 +00:00
|
|
|
feather.replace();
|
|
|
|
}
|
|
|
|
|
|
|
|
$('#visionSave').click(function() {
|
|
|
|
// update json from view
|
|
|
|
visionSettingsDisplay.ntmode = $('#visionClient').prop('checked') ? 'client' : 'server';
|
|
|
|
visionSettingsDisplay.team = parseInt($('#visionTeam').val(), 10);
|
|
|
|
visionSettingsDisplay.cameras.forEach(function (value, i) {
|
|
|
|
var camera = $('#camera' + i);
|
|
|
|
value.name = camera.find('.cameraName').val();
|
|
|
|
value.path = camera.find('.cameraPath').val();
|
|
|
|
value['pixel format'] = camera.find('.cameraPixelFormat').val();
|
|
|
|
value.width = parseInt(camera.find('.cameraWidth').val(), 10);
|
|
|
|
if (isNaN(value.width)) {
|
|
|
|
delete value["width"];
|
|
|
|
}
|
|
|
|
value.height = parseInt(camera.find('.cameraHeight').val(), 10);
|
|
|
|
if (isNaN(value.height)) {
|
|
|
|
delete value["height"];
|
|
|
|
}
|
|
|
|
value.fps = parseInt(camera.find('.cameraFps').val(), 10);
|
|
|
|
if (isNaN(value.fps)) {
|
|
|
|
delete value["fps"];
|
|
|
|
}
|
|
|
|
|
|
|
|
var brightness = camera.find('.cameraBrightness').val();
|
|
|
|
if (brightness !== '') {
|
|
|
|
value.brightness = parseInt(brightness);
|
|
|
|
if (isNaN(value.brightness)) {
|
|
|
|
value.brightness = brightness;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
delete value['brightness'];
|
|
|
|
}
|
|
|
|
|
|
|
|
var whiteBalance = camera.find('.cameraWhiteBalance').val();
|
|
|
|
if (whiteBalance !== '') {
|
|
|
|
value['white balance'] = parseInt(whiteBalance);
|
|
|
|
if (isNaN(value['white balance'])) {
|
|
|
|
value['white balance'] = whiteBalance;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
delete value['white balance'];
|
|
|
|
}
|
|
|
|
|
|
|
|
var exposure = camera.find('.cameraExposure').val();
|
|
|
|
if (exposure !== '') {
|
|
|
|
value.exposure = parseInt(exposure);
|
|
|
|
if (isNaN(value.exposure)) {
|
|
|
|
value.exposure = exposure;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
delete value['exposure'];
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
value.properties = JSON.parse(camera.find('.cameraProperties').val());
|
|
|
|
} catch (err) {
|
|
|
|
delete value['properties'];
|
|
|
|
}
|
2019-01-14 00:59:08 +00:00
|
|
|
|
|
|
|
value.stream = {'properties': []};
|
|
|
|
|
|
|
|
var streamWidth = parseInt(camera.find('.streamWidth').val(), 10);
|
|
|
|
if (!isNaN(streamWidth)) {
|
|
|
|
value.stream.properties.push({'name': 'width', 'value': streamWidth});
|
|
|
|
}
|
|
|
|
|
|
|
|
var streamHeight = parseInt(camera.find('.streamHeight').val(), 10);
|
|
|
|
if (!isNaN(streamHeight)) {
|
|
|
|
value.stream.properties.push({'name': 'height', 'value': streamHeight});
|
|
|
|
}
|
|
|
|
|
|
|
|
var streamFps = parseInt(camera.find('.streamFps').val(), 10);
|
|
|
|
if (!isNaN(streamFps)) {
|
|
|
|
value.stream.properties.push({'name': 'fps', 'value': streamFps});
|
|
|
|
}
|
|
|
|
|
|
|
|
var streamCompression = parseInt(camera.find('.streamCompression').val(), 10);
|
|
|
|
if (!isNaN(streamCompression)) {
|
|
|
|
value.stream.properties.push({'name': 'compression', 'value': streamCompression});
|
|
|
|
}
|
|
|
|
|
|
|
|
var streamDefaultCompression = parseInt(camera.find('.streamDefaultCompression').val(), 10);
|
|
|
|
if (!isNaN(streamDefaultCompression)) {
|
|
|
|
value.stream.properties.push({'name': 'default_compression', 'value': streamDefaultCompression});
|
|
|
|
}
|
2018-12-12 04:04:00 +00:00
|
|
|
});
|
2018-12-20 21:26:14 +00:00
|
|
|
var msg = {
|
|
|
|
type: 'visionSave',
|
|
|
|
settings: visionSettingsDisplay
|
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#visionDiscard').click(function() {
|
|
|
|
visionSettingsDisplay = $.extend(true, {}, visionSettingsServer);
|
|
|
|
updateVisionSettingsView();
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#addCamera').click(function() {
|
|
|
|
var i = visionSettingsDisplay.cameras.length;
|
|
|
|
visionSettingsDisplay.cameras.push({});
|
|
|
|
appendNewVisionCameraView({}, i);
|
2019-01-13 23:44:59 +00:00
|
|
|
updateCameraListView();
|
2018-12-20 21:26:14 +00:00
|
|
|
});
|
|
|
|
|
2019-01-13 23:44:59 +00:00
|
|
|
function updateCameraListView() {
|
|
|
|
var addConnectedDropdown = $('#addConnectedCameraList');
|
|
|
|
addConnectedDropdown.html('');
|
|
|
|
|
|
|
|
// disable all the alternate paths by default
|
|
|
|
visionSettingsDisplay.cameras.forEach(function (value, k) {
|
|
|
|
var cameraElem = $('#camera' + k);
|
|
|
|
cameraElem.find('.cameraConnectionBadge').removeClass('badge-dark').removeClass('badge-primary').addClass('badge-secondary').text('Disconnected');
|
|
|
|
cameraElem.find('.cameraAlternatePathsList').html('');
|
|
|
|
cameraElem.find('.cameraAlternatePaths').prop('disabled', true);
|
|
|
|
});
|
|
|
|
|
|
|
|
cameraList.forEach(function (camera, i) {
|
|
|
|
// See if one of the paths is an already existing camera
|
|
|
|
// Include the "main path" as the first path
|
|
|
|
var matchedCamera = false;
|
|
|
|
var paths = [camera.path];
|
|
|
|
camera.otherPaths.forEach(function (path, j) {
|
|
|
|
paths.push(path);
|
|
|
|
});
|
|
|
|
paths.forEach(function (path, j) {
|
|
|
|
visionSettingsDisplay.cameras.forEach(function (value, k) {
|
|
|
|
var cameraElem = $('#camera' + k);
|
|
|
|
var pathElem = cameraElem.find('.cameraPath');
|
|
|
|
if (path === pathElem.val()) {
|
|
|
|
matchedCamera = true;
|
|
|
|
|
|
|
|
// show camera as connected
|
|
|
|
cameraElem.find('.cameraConnectionBadge').removeClass('badge-dark').removeClass('badge-secondary').addClass('badge-primary').text('Connected');
|
|
|
|
|
|
|
|
// build alternate path list
|
|
|
|
var setAlternateDropdown = cameraElem.find('.cameraAlternatePathsList');
|
|
|
|
setAlternateDropdown.html('');
|
|
|
|
paths.forEach(function (altPath, j) {
|
|
|
|
setAlternateDropdown.append('<button class="dropdown-item cameraSetAlternatePath" type="button">' + altPath + '</button>');
|
|
|
|
});
|
|
|
|
|
|
|
|
cameraElem.find('.cameraAlternatePaths').prop('disabled', setAlternateDropdown.html() === '');
|
|
|
|
|
|
|
|
// hook up dropdown items to set alternate path
|
|
|
|
setAlternateDropdown.find('.cameraSetAlternatePath').click(function() {
|
|
|
|
pathElem.val($(this).text());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!matchedCamera) {
|
|
|
|
// add it to add connected camera list
|
|
|
|
addConnectedDropdown.append('<h5 class="dropdown-header">' + camera.name + '</h5>');
|
|
|
|
paths.forEach(function (path, j) {
|
|
|
|
addConnectedDropdown.append('<button class="dropdown-item addConnectedCameraItem" type="button">' + path + '</button>');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#addConnectedCamera').prop('disabled', addConnectedDropdown.html() === '');
|
|
|
|
|
|
|
|
// hook up dropdown items to create cameras
|
|
|
|
addConnectedDropdown.find('.addConnectedCameraItem').click(function() {
|
|
|
|
var i = visionSettingsDisplay.cameras.length;
|
|
|
|
var camera = {"path": $(this).text()};
|
|
|
|
visionSettingsDisplay.cameras.push(camera);
|
|
|
|
appendNewVisionCameraView(camera, i);
|
|
|
|
updateCameraListView();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-12-21 08:08:34 +00:00
|
|
|
var applicationFiles = [];
|
|
|
|
|
2018-12-20 21:26:14 +00:00
|
|
|
// Show details when appropriate for application type
|
|
|
|
function updateApplicationView() {
|
|
|
|
if ($('#applicationType').val().startsWith("upload")) {
|
|
|
|
$('#applicationUpload').collapse('show');
|
|
|
|
} else {
|
|
|
|
$('#applicationUpload').collapse('hide');
|
|
|
|
}
|
|
|
|
$('#applicationFile').val(null);
|
2018-12-21 08:08:34 +00:00
|
|
|
applicationFiles = [];
|
2018-12-12 04:04:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-20 21:26:14 +00:00
|
|
|
$('#applicationType').change(function() {
|
|
|
|
updateApplicationView();
|
|
|
|
});
|
|
|
|
|
2018-12-21 08:08:34 +00:00
|
|
|
$('#applicationFile').change(function() {
|
|
|
|
applicationFiles = this.files;
|
|
|
|
});
|
|
|
|
|
2018-12-20 21:26:14 +00:00
|
|
|
$('#applicationSave').click(function() {
|
|
|
|
var msg = {
|
|
|
|
type: 'applicationSave',
|
|
|
|
applicationType: $('#applicationType').val()
|
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
|
|
|
|
// upload the file if requested
|
2018-12-21 08:08:34 +00:00
|
|
|
if (applicationFiles.length <= 0) {
|
2018-12-20 21:26:14 +00:00
|
|
|
return;
|
2018-12-12 04:04:00 +00:00
|
|
|
}
|
2019-01-12 07:14:41 +00:00
|
|
|
|
2018-12-20 21:26:14 +00:00
|
|
|
$('#applicationSave').button('loading');
|
2019-01-12 07:14:41 +00:00
|
|
|
|
|
|
|
var msg = {
|
|
|
|
type: 'applicationStartUpload',
|
|
|
|
applicationType: $('#applicationType').val()
|
2018-12-12 04:04:00 +00:00
|
|
|
};
|
2019-01-12 07:14:41 +00:00
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
|
|
|
|
var reader = new FileReader();
|
|
|
|
var file = applicationFiles.item(0);
|
|
|
|
|
|
|
|
function uploadFile(start) {
|
|
|
|
var nextSlice = start + (64 * 1024) + 1;
|
|
|
|
reader.onloadend = function(e) {
|
|
|
|
if (e.target.readyState !== FileReader.DONE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
connection.send(e.target.result);
|
|
|
|
if (nextSlice < file.size) {
|
|
|
|
// more to go
|
|
|
|
uploadFile(nextSlice);
|
|
|
|
} else {
|
|
|
|
// done
|
|
|
|
var msg = {
|
|
|
|
type: 'applicationFinishUpload',
|
|
|
|
applicationType: $('#applicationType').val()
|
|
|
|
};
|
|
|
|
connection.send(JSON.stringify(msg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reader.readAsArrayBuffer(file.slice(start, nextSlice));
|
|
|
|
}
|
|
|
|
uploadFile(0);
|
2018-12-12 04:04:00 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Start with display disconnected and start initial connection attempt
|
|
|
|
displayDisconnected();
|
2018-12-20 21:26:14 +00:00
|
|
|
updateNetworkSettingsView();
|
2018-12-12 04:04:00 +00:00
|
|
|
updateVisionSettingsView();
|
2018-12-20 21:26:14 +00:00
|
|
|
updateApplicationView();
|
2018-12-12 04:04:00 +00:00
|
|
|
connect();
|