/**
* Global variables and functions used in the state of each session emulation
* @module config */
// global Mqtt variable
var mqtt;
// definition of the variable for topic susbscription to publish messages
var mqttTopicToPublish;
// definition of the variable timers for set intervals pubilsh messages (parametrized)
var mqttTimerFullUpdate;
var mqttTimerPartialUpdate;
var mqttMessagesPerTimer;
// flag variable to set necessity of update antennas and anchors states
var needUpdateAntennas = false;
var needUpdateAnchors = false;
// flag variable to prevent double animations
var animatingAssetPointFlags = [false];
// flag variable to set connected state (connected/disconnected)
var connectedFlag = false;
// list variable to hold selected ml agents
var selected_ml_agent_algorithm = [];
// MQTT connect field variables
var connection_string = "";
// backend read rate var
var backend_read_rate;
// set variables to Path Loss Model [RSSI] (parametrized)
var pathLossExpoent; // assuming n = 2
var txPower; // assuming an txPower 20 dBm
var referenceDistance; // assuming d0 = 1 meter
var constantFading; // min and max values of Gaussian distribution, Xσ
var skewIndex; // skew index applied to Gaussian distribution, σ
var attenuationFactor; // 1 dBm factor by intersetion wall
// flags variables to set buttons states (activate/deactivate)
var coordinatesPointContainerFlag = 0;
var distanceRssiContainerFlag = 0;
var animationOptionsContainerFlag = 0;
var consoleDebuggerContainerFlag = 0;
var rssiPathLossModelContainerFlag = 0;
var mqttVariablesContainerFlag = 0;
var mlAgentsContainerFlag = 0;
// ========== End of global variables used in the state of each session emulation ========== //
// ========== Splash Page code ========== //
// Populate dropdown list of maps
var map_select = document.getElementById("map_select");
var map_options = [
'Library - Aveiro University - Aveiro, Portugal'
];
for (var i = 0; i < map_options.length; i++) {
var opt = map_options[i];
var el = document.createElement("option");
el.textContent = opt;
el.value = opt;
map_select.appendChild(el);
}
// Add click eventListner to enter Button and hide Splash Page
var splashScreen = document.querySelector('.splash');
var mapSelectorConfirmButton = document.getElementById('map_select_enter');
mapSelectorConfirmButton.addEventListener('click', () => {
splashScreen.style.opacity = 0;
setTimeout(() => {
splashScreen.classList.add('hidden')
}, 500)
// call main() after confirm map selection
mainMethod();
})
// ========== End of Splash Page Code ========== //
// ========== Generic Functions used in session simulation ========== //
/**
* Load a JSON object from the path
* @param {string} url URL string path
* @param {XMLHttpRequest} callback Callback function
*/
function loadJsonFile(url, callback) {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', url, true); // Replace 'my_data' with the path to your file
xobj.onreadystatechange = function () {
if (xobj.readyState == 4 && xobj.status == "200") {
// Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
callback(xobj.responseText);
}
};
xobj.send(null);
}
/**
* Assign Variable Parameters from JSON file
* @param {JSON} jsonData JSON Data object
*/
function assignVariableParameters(jsonData) {
// mqtt_connection_parameters
document.getElementById("mqtt_hostname_input").value = jsonData.mqtt_connection_parameters.hostname;
document.getElementById("mqtt_port_input").value = jsonData.mqtt_connection_parameters.port;
document.getElementById("mqtt_username_input").value = jsonData.mqtt_connection_parameters.username;
document.getElementById("mqtt_password_input").value = jsonData.mqtt_connection_parameters.password;
document.getElementById("topic_publish_input").value = jsonData.mqtt_connection_parameters.publish_topic;
document.getElementById("topic_receive_predictions_input").value = jsonData.mqtt_connection_parameters.predictions_topic;
document.getElementById("topic_receive_calculations_input").value = jsonData.mqtt_connection_parameters.calculations_topic;
document.getElementById("reconnect_timeout_input").value = jsonData.mqtt_connection_parameters.reconnect_timeout;
document.getElementById("mqtt_timer_full_update").value = jsonData.mqtt_connection_parameters.timer_full_update;
document.getElementById("mqtt_timer_partial_update").value = jsonData.mqtt_connection_parameters.timer_partial_update;
document.getElementById("mqtt_messages_per_timer").value = jsonData.mqtt_connection_parameters.messages_per_timer;
document.getElementById("backend_read_rate_input").value = jsonData.mqtt_connection_parameters.backend_read_rate;
// ml_agents_parameters
document.getElementById("KNN-label").textContent = jsonData.ml_agents_parameters.k_nearest_neighbor;
document.getElementById("SVR-label").textContent = jsonData.ml_agents_parameters.support_vector_regression;
document.getElementById("GBR-label").textContent = jsonData.ml_agents_parameters.gradient_boost_regression;
document.getElementById("RF-label").textContent = jsonData.ml_agents_parameters.random_forest;
document.getElementById("DT-label").textContent = jsonData.ml_agents_parameters.decision_tree;
// rssi_path_loss_parameters
document.getElementById("reference_distance_parameter").value = jsonData.rssi_path_loss_parameters.reference_distance;
document.getElementById("path_loss_parameter").value = jsonData.rssi_path_loss_parameters.path_loss_expoent;
document.getElementById("transmission_power_parameter").value = jsonData.rssi_path_loss_parameters.transmission_power;
document.getElementById("gaussian_distribuition_parameter").value = jsonData.rssi_path_loss_parameters.gaussian_distribuition;
document.getElementById("skew_index_parameter").value = jsonData.rssi_path_loss_parameters.skew_index;
document.getElementById("attenuation_factor_parameter").value = jsonData.rssi_path_loss_parameters.attenuation_factor;
}
/**
* Return JSON object from given URL sting path
* @param {string} url URL string path
* @return {JSON} GeoJSON Data Object
*/
async function getGeoJsonObject(url) {
return await fetch(url)
.then(result => result.json())
}
/**
* Generate UUID string
* @return {string} Generated 16-characters UUID string
*/
function generateUUID() {
var d = new Date().getTime(); //Timestamp
var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || 0; //Time in microseconds since page-load or 0 if unsupported
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16; //random number between 0 and 16
if (d > 0) { //Use timestamp until depleted
r = (d + r) % 16 | 0;
d = Math.floor(d / 16);
} else { //Use microseconds since page-load if supported
r = (d2 + r) % 16 | 0;
d2 = Math.floor(d2 / 16);
}
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
/**
* Get a random number between max and min arguments
* @param {number} min minimum number
* @param {number} max maximum number
* @return {number} Math.random() between max and min arguments
*/
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
/**
* Get bearing between two point locations
* @typedef {Object} LongLat with Longitude and Latitude coordinates
* @property {number} Long - Longitude value
* @property {number} Lat - Latitude value
* @param {LongLat} LongLat1 - Longitude and Latitude coordinates of point 1
* @param {LongLat} LongLat2 - Longitude and Latitude coordinates of point 2
* @return {number} Bearing between the both point locations
*/
function getBearingBetweenLocations(LongLat1, LongLat2) {
// convert Long and Lat to Radians
var long1 = LongLat1[0] * Math.PI / 180;
var lat1 = LongLat1[1] * Math.PI / 180;
var long2 = LongLat2[0] * Math.PI / 180;
var lat2 = LongLat2[1] * Math.PI / 180;
// calculate differences
var deltaLong = (long2 - long1);
var deltaLat = (lat2 - lat1);
// get angle in randians
var brng = Math.atan2(deltaLat, deltaLong);
// convert to degrees, rotate -90deg and restrict angle from -180deg to 180deg
brng = rescrictTo180Degrees(brng * 180 / Math.PI - 90);
return brng;
}
/**
* Remove an item occurrency from an array
* @param {Array} arr Array from where we want remove element
* @param {any} value Element to be removed
* @returns {Array} Argument array without removed element
*/
function removeItemOnce(arr, value) {
var index = arr.indexOf(value);
if (index > -1) {
arr.splice(index, 1);
}
return arr;
}
/**
* Remove all item occurrencies from an array
* @param {Array} arr Array from where we want remove element
* @param {any} value Element to be removed
* @returns {Array} Argument array without all occurrencies of removed element
*/
function removeItemAll(arr, value) {
var i = 0;
while (i < arr.length) {
if (arr[i] === value) {
arr.splice(i, 1);
} else {
++i;
}
}
return arr;
}
/**
* Rectrict the value of an angle to be between -180° to 180° (continuous mapping problem)
* @param {number} value Value where to apply the restriction
* @returns {number} Angle restricted between -180° to 180°
*/
function rescrictTo180Degrees(value) {
// reduce the angle
var angle = parseFloat(value) % 360;
// force it to be the positive remainder, so that 0 <= angle < 360
angle = (angle + 360) % 360;
// force into the minimum absolute value residue class, so that -180 < angle <= 180
if (angle > 180)
angle -= 360;
return angle;
}
/**
* Conversion of Latitude and Longitude, from Decimal Degrees Format to DMS [Degrees° Minutes' Secconds"]
* @param {number} coordinate Value where to apply the conversion
* @returns {string} Coverted value to DMS [Degrees° Minutes' Secconds"]
*/
function toDegreesMinutesAndSeconds(coordinate) {
var absolute = Math.abs(coordinate);
var degrees = Math.floor(absolute);
var minutes = Math.floor((absolute - degrees) * 60);
var seconds = ((absolute - degrees - minutes / 60) * Math.pow(60, 2)).toFixed(2);
var deg_symbol = String.fromCharCode(176); // degree symbol (°)
return degrees + deg_symbol + " " + minutes + "' " + seconds + """;
}
/**
* Convert Longitude from Decimal Degree Format to DMS (Degrees° Minutes' Seconds")
* @param {number} lng Longitude value where to apply the conversion
* @returns {string} Coverted longitude value to DMS [Degrees° Minutes' Secconds"]
*/
function convertDMSLng(lng) {
var longitude = toDegreesMinutesAndSeconds(lng);
var longitudeCardinal = lng >= 0 ? "E" : "W";
return longitude + " " + longitudeCardinal;
}
/**
* Convert Latitude from Decimal Degree Format to DMS (Degrees° Minutes' Seconds")
* @param {number} lat Latitude value where to apply the conversion
* @returns {string} Coverted longitude value to DMS [Degrees° Minutes' Secconds"]
*/
function convertDMSLat(lat) {
var latitude = toDegreesMinutesAndSeconds(lat);
var latitudeCardinal = lat >= 0 ? "N" : "S";
return latitude + " " + latitudeCardinal;
}
/**
* Definition of Geocoder Search Bar
* @param {string} query Query string to search
* @param {Array} mapFeaturesData Array of Map features
* @returns {Array} Array of queried matching features
*/
function forwardGeocoder(query, mapFeaturesData) {
var matchingFeatures = [];
for (var i = 0, len = mapFeaturesData.features.length; i < len; i++) {
var feature = mapFeaturesData.features[i];
if (feature.properties.hasOwnProperty('name') &&
feature.properties['name']
.toLowerCase()
.search(query.toLowerCase()) !== -1
) {
feature['place_name'] = feature.properties['name'];
feature['center'] = turf.centroid(feature).geometry.coordinates;
feature['place_type'] = ['park'];
matchingFeatures.push(feature);
} else if (feature.properties.hasOwnProperty('name') &&
feature.properties.name
.toLowerCase()
.search(query.toLowerCase()) !== -1
) {
feature['place_name'] = feature.properties.name;
feature['center'] = turf.centroid(feature).geometry.coordinates;
feature['place_type'] = ['park'];
matchingFeatures.push(feature);
}
}
return matchingFeatures;
}
/**
* Catch Walls Of Map Features Data array and filter
* @param {Array} mapFeaturesData Array of Map features
* @param {Array} geojson_walls Array of Map walls features
* @param {Array} filter_walls_keywords Array of keywords to filter Map walls features
* @param {Array} filter_walls_level Array of levels to filter Map walls features
* @returns {Array} Array of geojson_walls filtered by arguments keywords and levels
*/
function catchWallsOfMapFeaturesData(mapFeaturesData, geojson_walls, filter_walls_keywords, filter_walls_level) {
// deal with obstacle walls
mapFeaturesData.features.forEach(index => {
// apply polygon to line to define walls
wall = turf.polygonToLine(index)
// apply filter data (for the given "words" on the given "levels")
filter_walls_level.forEach(level => {
filter_walls_keywords.forEach(word => {
// apply filter data (ex policlínica: for "walls" and "glass", only on level 0)
if (wall.properties.name.includes(word) && wall.properties.level.includes(level)) {
geojson_walls.features.push(wall);
}
})
})
});
}
// ========== End of generic functions used in session simulation ========== //
Source