<commit_before>angular.module('anol.draw')
/**
 * @ngdoc directive
 * @name anol.draw.anolDraw
 *
 * @requires $compile
 * @requires $rootScope
 * @requires $translate
 * @requires anol.map.MapService
 * @requires anol.map.ControlsSerivce
 * @requries anol.map.DrawService
 *
 * @param {boolean} continueDrawing Don't deactivate drawing after feature is added
 * @param {string} pointTooltipPlacement Position of point tooltip
 * @param {string} lineTooltipPlacement Position of line tooltip
 * @param {string} polygonTooltipPlacement Position of polygon tooltip
 * @param {number} tooltipDelay Time in milisecounds to wait before display tooltip
 * @param {boolean} tooltipEnable Enable tooltips. Default true for non-touch screens, default false for touchscreens
 * @param {string} templateUrl Url to template to use instead of default one
 *
 * @description
 * Provides controls to draw points, lines and polygons, modify and remove them
 */
.directive('anolDraw', ['$compile', '$rootScope', '$translate', '$timeout', 'ControlsService', 'MapService', 'DrawService',
    function($compile, $rootScope, $translate, $timeout, ControlsService, MapService, DrawService) {
    return {
        restrict: 'A',
        require: ['?^anolFeaturePropertiesEditor', '?^anolFeatureStyleEditor'],
        scope: {
            continueDrawing: '@',
            tooltipDelay: '@',
            tooltipEnable: '@',
            pointTooltipPlacement: '@',
            lineTooltipPlacement: '@',
            polygonTooltipPlacement: '@'
        },
        templateUrl: function(tElement, tAttrs) {
            var defaultUrl = 'src/modules/draw/templates/draw.html';
            return tAttrs.templateUrl || defaultUrl;
        },
        link: function(scope, element, attrs, controllers) {
            var AnolFeaturePropertiesEditor = controllers[0];
            var AnolFeatureStyleEditor = controllers[1];

            // attribute defaults
            scope.continueDrawing = angular.isDefined(scope.continueDrawing) ?
                scope.continueDrawing : false;
            scope.tooltipEnable = angular.isDefined(scope.tooltipEnable) ?
                scope.tooltipEnable : !ol.has.TOUCH;
            scope.tooltipDelay = angular.isDefined(scope.tooltipDelay) ?
                scope.tooltipDelay : 500;
            scope.pointTooltipPlacement = angular.isDefined(scope.pointTooltipPlacement) ?
                scope.pointTooltipPlacement : 'right';
            scope.lineTooltipPlacement = angular.isDefined(scope.lineTooltipPlacement) ?
                scope.lineTooltipPlacement : 'right';
            scope.polygonTooltipPlacement = angular.isDefined(scope.polygonTooltipPlacement) ?
                scope.polygonTooltipPlacement : 'right';

            var activeLayer;
            var selectedFeature;
            var drawPointControl, drawLineControl, drawPolygonControl, modifyControl;

            // disabled by default. Will be enabled, when feature selected
            var removeButtonElement = element.find('.draw-remove');
            removeButtonElement.addClass('disabled');

            var createDrawInteractions = function(drawType, source, control) {
                // create draw interaction
                var draw = new ol.interaction.Draw({
                    source: source,
                    type: drawType
                });

                if(scope.continueDrawing === false) {
                    draw.on('drawend', function() {
                        // TODO remove when https://github.com/openlayers/ol3/issues/3610/ resolved
                        $timeout(function() { control.deactivate(); }, 275);
                    });
                }

                // TODO how to use both?
                // TODO check if we can remove this!
                if(angular.isObject(AnolFeaturePropertiesEditor)) {
                    draw.on('drawend', function(evt) {
                        var feature = evt.feature;
                        AnolFeaturePropertiesEditor.editFeature(feature);
                    });
                } else if (angular.isObject(AnolFeatureStyleEditor)) {
                    draw.on('drawend', function(evt) {
                        var feature = evt.feature;
                        AnolFeatureStyleEditor.editFeature(feature);
                    });
                }
                return [draw];
            };

            var createModifyInteractions = function(layer) {
                var selectInteraction = new ol.interaction.Select({
                    toggleCondition: ol.events.condition.never,
                    layers: [layer]
                });
                selectInteraction.on('select', function(evt) {
                    if(evt.selected.length === 0) {
                        selectedFeature = undefined;
                        removeButtonElement.addClass('disabled');
                    } else {
                        selectedFeature = evt.selected[0];
                        removeButtonElement.removeClass('disabled');
                    }
                });
                var modifyInteraction = new ol.interaction.Modify({
                    features: selectInteraction.getFeatures()
                });
                var snapInteraction = new ol.interaction.Snap({
                    source: layer.getSource()
                });
                return [selectInteraction, modifyInteraction, snapInteraction];
            };

            var createDrawControl = function(controlElement, controlTarget) {
                var drawControl = new anol.control.Control({
                    element: controlElement,
                    target: controlTarget,
                    exclusive: true,
                    disabled: true
                });
                drawControl.onDeactivate(deactivate, scope);
                drawControl.onActivate(activate, scope);
                return drawControl;
            };

            var createModifyControl = function(controlElement, controlTarget) {
                var _modifyControl = new anol.control.Control({
                    element: controlElement,
                    target: controlTarget,
                    exclusive: true,
                    disabled: true
                });
                _modifyControl.onDeactivate(deactivate);
                _modifyControl.onActivate(activate);
                _modifyControl.onDeactivate(function() {
                    selectedFeature = undefined;
                });
                return _modifyControl;
            };

            var deactivate = function(targetControl) {
                angular.forEach(targetControl.interactions, function(interaction) {
                    interaction.setActive(false);
                });
            };

            var activate = function(targetControl) {
                angular.forEach(targetControl.interactions, function(interaction) {
                    interaction.setActive(true);
                });
            };

            // Button binds
            scope.drawPoint = function() {
                if(drawPointControl.disabled === true) {
                    return;
                }
                if(drawPointControl.active) {
                    drawPointControl.deactivate();
                } else {
                    drawPointControl.activate();
                }
            };

            scope.drawLine = function() {
                if(drawLineControl.disabled === true) {
                    return;
                }
                if(drawLineControl.active) {
                    drawLineControl.deactivate();
                } else {
                    drawLineControl.activate();
                }
            };

            scope.drawPolygon = function() {
                if(drawPolygonControl.disabled === true) {
                    return;
                }
                if(drawPolygonControl.active) {
                    drawPolygonControl.deactivate();
                } else {
                    drawPolygonControl.activate();
                }
            };

            scope.modify = function() {
                if(modifyControl.disabled === true) {
                    return;
                }
                if(modifyControl.active) {
                    modifyControl.deactivate();
                } else {
                    modifyControl.activate();
                }
            };

            scope.remove = function() {
                if(selectedFeature !== undefined) {
                    activeLayer.olLayer.getSource().removeFeature(selectedFeature);
                    modifyControl.interactions[0].getFeatures().clear();
                    selectedFeature = undefined;
                    removeButtonElement.addClass('disabled');
                }
            };

            scope.map = MapService.getMap();

            element.addClass('ol-control');
            element.addClass('anol-draw');

            var drawControl = new anol.control.Control({
                element: element
            });

            drawPointControl = createDrawControl(
                element.find('.draw-point'),
                element
            );

            drawLineControl = createDrawControl(
                element.find('.draw-line'),
                element
            );

            drawPolygonControl = createDrawControl(
                element.find('.draw-polygon'),
                element
            );

            modifyControl = createModifyControl(
                element.find('.draw-modify'),
                element
            );

            ControlsService.addControls([
                drawControl, drawPointControl, drawLineControl,
                drawPolygonControl, modifyControl
            ]);

            var allInteractions = function() {
                return drawPointControl.interactions
                    .concat(drawLineControl.interactions)
                    .concat(drawPolygonControl.interactions)
                    .concat(modifyControl.interactions);
            };

            var bindActiveLayer = function(layer) {
                drawPointControl.interactions = createDrawInteractions('Point', layer.olLayer.getSource(), drawPointControl);
                drawPointControl.enable();
                drawLineControl.interactions = createDrawInteractions('LineString', layer.olLayer.getSource(), drawLineControl);
                drawLineControl.enable();
                drawPolygonControl.interactions = createDrawInteractions('Polygon', layer.olLayer.getSource(), drawPolygonControl);
                drawPolygonControl.enable();
                modifyControl.interactions = createModifyInteractions(layer.olLayer);
                modifyControl.enable();

                angular.forEach(allInteractions(), function(interaction) {
                    interaction.setActive(false);
                    scope.map.addInteraction(interaction);
                });

                activeLayer = layer;
            };

            var unbindActiveLayer = function() {
                angular.forEach(allInteractions(), function(interaction) {
                    interaction.setActive(false);
                    scope.map.removeInteraction(interaction);
                });

                drawPointControl.interactions = [];
                drawPointControl.disable();
                drawLineControl.interactions = [];
                drawLineControl.disable();
                drawPolygonControl.interactions = [];
                drawPolygonControl.disable();
                modifyControl.interactions = [];
                modifyControl.disable();

                activeLayer = undefined;
            };

            scope.$watch(function() {
                return DrawService.activeLayer;
            }, function(newActiveLayer, oldActiveLayer) {
                if(newActiveLayer === activeLayer) {
                    return;
                }
                if(oldActiveLayer !== undefined) {
                    unbindActiveLayer();
                }
                if(newActiveLayer !== undefined) {
                    bindActiveLayer(newActiveLayer);
                }
            });
        }
    };
}]);
<commit_msg>Added snapped drawing<commit_after>angular.module('anol.draw')
/**
 * @ngdoc directive
 * @name anol.draw.anolDraw
 *
 * @requires $compile
 * @requires $rootScope
 * @requires $translate
 * @requires anol.map.MapService
 * @requires anol.map.ControlsSerivce
 * @requries anol.map.DrawService
 *
 * @param {boolean} continueDrawing Don't deactivate drawing after feature is added
 * @param {boolean} freeDrawing Deactivate snapped drawing
 * @param {string} pointTooltipPlacement Position of point tooltip
 * @param {string} lineTooltipPlacement Position of line tooltip
 * @param {string} polygonTooltipPlacement Position of polygon tooltip
 * @param {number} tooltipDelay Time in milisecounds to wait before display tooltip
 * @param {boolean} tooltipEnable Enable tooltips. Default true for non-touch screens, default false for touchscreens
 * @param {string} templateUrl Url to template to use instead of default one
 *
 * @description
 * Provides controls to draw points, lines and polygons, modify and remove them
 */
.directive('anolDraw', ['$compile', '$rootScope', '$translate', '$timeout', 'ControlsService', 'MapService', 'DrawService',
    function($compile, $rootScope, $translate, $timeout, ControlsService, MapService, DrawService) {
    return {
        restrict: 'A',
        require: ['?^anolFeaturePropertiesEditor', '?^anolFeatureStyleEditor'],
        scope: {
            continueDrawing: '@',
            freeDrawing: '@',
            tooltipDelay: '@',
            tooltipEnable: '@',
            pointTooltipPlacement: '@',
            lineTooltipPlacement: '@',
            polygonTooltipPlacement: '@'
        },
        templateUrl: function(tElement, tAttrs) {
            var defaultUrl = 'src/modules/draw/templates/draw.html';
            return tAttrs.templateUrl || defaultUrl;
        },
        link: function(scope, element, attrs, controllers) {
            var AnolFeaturePropertiesEditor = controllers[0];
            var AnolFeatureStyleEditor = controllers[1];

            // attribute defaults
            scope.continueDrawing = angular.isDefined(scope.continueDrawing) ?
                scope.continueDrawing : false;
            scope.freeDrawing = angular.isDefined(scope.freeDrawing) ?
                scope.freeDrawing : false;
            scope.tooltipEnable = angular.isDefined(scope.tooltipEnable) ?
                scope.tooltipEnable : !ol.has.TOUCH;
            scope.tooltipDelay = angular.isDefined(scope.tooltipDelay) ?
                scope.tooltipDelay : 500;
            scope.pointTooltipPlacement = angular.isDefined(scope.pointTooltipPlacement) ?
                scope.pointTooltipPlacement : 'right';
            scope.lineTooltipPlacement = angular.isDefined(scope.lineTooltipPlacement) ?
                scope.lineTooltipPlacement : 'right';
            scope.polygonTooltipPlacement = angular.isDefined(scope.polygonTooltipPlacement) ?
                scope.polygonTooltipPlacement : 'right';

            var activeLayer;
            var selectedFeature;
            var drawPointControl, drawLineControl, drawPolygonControl, modifyControl;

            // disabled by default. Will be enabled, when feature selected
            var removeButtonElement = element.find('.draw-remove');
            removeButtonElement.addClass('disabled');

            var createDrawInteractions = function(drawType, source, control, layer) {
                // create draw interaction
                var draw = new ol.interaction.Draw({
                    source: source,
                    type: drawType
                });

                if(scope.continueDrawing === false) {
                    draw.on('drawend', function() {
                        // TODO remove when https://github.com/openlayers/ol3/issues/3610/ resolved
                        $timeout(function() { control.deactivate(); }, 275);
                    });
                }

                // TODO how to use both?
                // TODO check if we can remove this!
                if(angular.isObject(AnolFeaturePropertiesEditor)) {
                    draw.on('drawend', function(evt) {
                        var feature = evt.feature;
                        AnolFeaturePropertiesEditor.editFeature(feature);
                    });
                } else if (angular.isObject(AnolFeatureStyleEditor)) {
                    draw.on('drawend', function(evt) {
                        var feature = evt.feature;
                        AnolFeatureStyleEditor.editFeature(feature);
                    });
                }
                var interactions = [draw];
                if(scope.freeDrawing !== false) {
                    var snapInteraction = new ol.interaction.Snap({
                        source: layer.getSource()
                    });
                    interactions.push(snapInteraction);
                }
                return interactions;
            };

            var createModifyInteractions = function(layer) {
                var selectInteraction = new ol.interaction.Select({
                    toggleCondition: ol.events.condition.never,
                    layers: [layer]
                });
                selectInteraction.on('select', function(evt) {
                    if(evt.selected.length === 0) {
                        selectedFeature = undefined;
                        removeButtonElement.addClass('disabled');
                    } else {
                        selectedFeature = evt.selected[0];
                        removeButtonElement.removeClass('disabled');
                    }
                });
                var modifyInteraction = new ol.interaction.Modify({
                    features: selectInteraction.getFeatures()
                });
                var snapInteraction = new ol.interaction.Snap({
                    source: layer.getSource()
                });
                return [selectInteraction, modifyInteraction, snapInteraction];
            };

            var createDrawControl = function(controlElement, controlTarget) {
                var drawControl = new anol.control.Control({
                    element: controlElement,
                    target: controlTarget,
                    exclusive: true,
                    disabled: true
                });
                drawControl.onDeactivate(deactivate, scope);
                drawControl.onActivate(activate, scope);
                return drawControl;
            };

            var createModifyControl = function(controlElement, controlTarget) {
                var _modifyControl = new anol.control.Control({
                    element: controlElement,
                    target: controlTarget,
                    exclusive: true,
                    disabled: true
                });
                _modifyControl.onDeactivate(deactivate);
                _modifyControl.onActivate(activate);
                _modifyControl.onDeactivate(function() {
                    selectedFeature = undefined;
                });
                return _modifyControl;
            };

            var deactivate = function(targetControl) {
                angular.forEach(targetControl.interactions, function(interaction) {
                    interaction.setActive(false);
                });
            };

            var activate = function(targetControl) {
                angular.forEach(targetControl.interactions, function(interaction) {
                    interaction.setActive(true);
                });
            };

            // Button binds
            scope.drawPoint = function() {
                if(drawPointControl.disabled === true) {
                    return;
                }
                if(drawPointControl.active) {
                    drawPointControl.deactivate();
                } else {
                    drawPointControl.activate();
                }
            };

            scope.drawLine = function() {
                if(drawLineControl.disabled === true) {
                    return;
                }
                if(drawLineControl.active) {
                    drawLineControl.deactivate();
                } else {
                    drawLineControl.activate();
                }
            };

            scope.drawPolygon = function() {
                if(drawPolygonControl.disabled === true) {
                    return;
                }
                if(drawPolygonControl.active) {
                    drawPolygonControl.deactivate();
                } else {
                    drawPolygonControl.activate();
                }
            };

            scope.modify = function() {
                if(modifyControl.disabled === true) {
                    return;
                }
                if(modifyControl.active) {
                    modifyControl.deactivate();
                } else {
                    modifyControl.activate();
                }
            };

            scope.remove = function() {
                if(selectedFeature !== undefined) {
                    activeLayer.olLayer.getSource().removeFeature(selectedFeature);
                    modifyControl.interactions[0].getFeatures().clear();
                    selectedFeature = undefined;
                    removeButtonElement.addClass('disabled');
                }
            };

            scope.map = MapService.getMap();

            element.addClass('ol-control');
            element.addClass('anol-draw');

            var drawControl = new anol.control.Control({
                element: element
            });

            drawPointControl = createDrawControl(
                element.find('.draw-point'),
                element
            );

            drawLineControl = createDrawControl(
                element.find('.draw-line'),
                element
            );

            drawPolygonControl = createDrawControl(
                element.find('.draw-polygon'),
                element
            );

            modifyControl = createModifyControl(
                element.find('.draw-modify'),
                element
            );

            ControlsService.addControls([
                drawControl, drawPointControl, drawLineControl,
                drawPolygonControl, modifyControl
            ]);

            var allInteractions = function() {
                return drawPointControl.interactions
                    .concat(drawLineControl.interactions)
                    .concat(drawPolygonControl.interactions)
                    .concat(modifyControl.interactions);
            };

            var bindActiveLayer = function(layer) {
                drawPointControl.interactions = createDrawInteractions(
                    'Point', layer.olLayer.getSource(), drawPointControl, layer.olLayer);
                drawPointControl.enable();
                drawLineControl.interactions = createDrawInteractions(
                    'LineString', layer.olLayer.getSource(), drawLineControl, layer.olLayer);
                drawLineControl.enable();
                drawPolygonControl.interactions = createDrawInteractions(
                    'Polygon', layer.olLayer.getSource(), drawPolygonControl, layer.olLayer);
                drawPolygonControl.enable();
                modifyControl.interactions = createModifyInteractions(layer.olLayer);
                modifyControl.enable();

                angular.forEach(allInteractions(), function(interaction) {
                    interaction.setActive(false);
                    scope.map.addInteraction(interaction);
                });

                activeLayer = layer;
            };

            var unbindActiveLayer = function() {
                angular.forEach(allInteractions(), function(interaction) {
                    interaction.setActive(false);
                    scope.map.removeInteraction(interaction);
                });

                drawPointControl.interactions = [];
                drawPointControl.disable();
                drawLineControl.interactions = [];
                drawLineControl.disable();
                drawPolygonControl.interactions = [];
                drawPolygonControl.disable();
                modifyControl.interactions = [];
                modifyControl.disable();

                activeLayer = undefined;
            };

            scope.$watch(function() {
                return DrawService.activeLayer;
            }, function(newActiveLayer, oldActiveLayer) {
                if(newActiveLayer === activeLayer) {
                    return;
                }
                if(oldActiveLayer !== undefined) {
                    unbindActiveLayer();
                }
                if(newActiveLayer !== undefined) {
                    bindActiveLayer(newActiveLayer);
                }
            });
        }
    };
}]);
<|endoftext|>


<commit_before>import * as metricActions from 'gModules/metrics/actions'
import {runSimulations} from 'gModules/simulations/actions'

import e from 'gEngine/engine'

import {isAtLocation, isWithinRegion, getBounds, move, translate} from 'lib/locationUtils'

const DYNAMIC_FILL_TYPES = ['FUNCTION']

// TODO(matthew): Dry up code with this and copy and undo perhaps.
function translateReadableIds(input, idMap) {
  if (!input) {return ""}
  const re = RegExp(Object.keys(idMap).join("|"), "g")
  return input.replace(re, (match) => idMap[match])
}

const getDirAndLen = (start, end) => ({
  direction: {row: Math.sign(end.row - start.row), column: Math.sign(end.column - start.column)},
  length: Math.abs(start.row - end.row) || Math.abs(start.column - end.column),
})

function fillFunction(startMetric, startGuesstimate, metrics, idMap2, newLocation) {
  const metric = {...startMetric, ...e.metric.create(Object.keys(idMap2)), location: newLocation}
  const {input} = startGuesstimate
  const translateFn = translate(startMetric.location, newLocation)
  let idMap = {}
  metrics.forEach(m => {
    const matchedMetric = metrics.find(m2 => isAtLocation(translateFn(m.location), m2.location))
    if (!!matchedMetric) { idMap[m.readableId] = matchedMetric.readableId }
  })

  return { metric, guesstimate: {...startGuesstimate, metric: metric.id, input: translateReadableIds(input, idMap)} }
}

function addAtLoc(startMetric, startGuesstimate, metrics, idMap, newLocation) {
  const metric = {...startMetric, ...e.metric.create(Object.keys(idMap)), location: newLocation}
  return { metric, guesstimate: {...startGuesstimate, metric: metric.id} }
}

function buildNewMetrics(startMetric, startGuesstimate, startLocation, {direction, length}, metrics, guesstimates) {
  const {guesstimateType, input} = startGuesstimate

  let newMetrics = []
  let newGuesstimates = []

  const translateFn = DYNAMIC_FILL_TYPES.includes(guesstimateType) ?
    (loc, metrics, idMap) => fillFunction(startMetric, startGuesstimate, metrics, idMap, loc)
      :
    (loc, metrics, idMap) => addAtLoc(startMetric, startGuesstimate, metrics, idMap, loc)

  let idMap = {}
  metrics.forEach(m => {idMap[m.readableId] = m.id})

  let currLocation = move(startLocation, direction)
  for (var i = 0; i < length; i++) {
    const {metric, guesstimate} = translateFn(currLocation, metrics.concat(newMetrics), idMap)
    idMap[metric.readableId] = metric.id
    newMetrics.push(metric)
    newGuesstimates.push(guesstimate)
    currLocation = move(currLocation, direction)
  }

  return {newMetrics, newGuesstimates}
}

export function fillRegion(spaceId, {start, end}) {
  return (dispatch, getState) => {
    const state = getState()

    const metrics = state.metrics.filter(m => m.space === spaceId)
    const guesstimates = state.guesstimates.filter(g => _.some(metrics, m => m.id === g.metric))

    const startMetric = metrics.find(m => isAtLocation(m.location, start))

    const containedMetrics = metrics.filter(m => (!startMetric || m.id !== startMetric.id) && isWithinRegion(m.location, getBounds({start, end})))
    dispatch(metricActions.removeMetrics(containedMetrics.map(m => m.id)))

    if (!startMetric) { return }

    const startGuesstimate = guesstimates.find(g => g.metric === startMetric.id)

    const {newMetrics, newGuesstimates} = buildNewMetrics(startMetric, startGuesstimate, start, getDirAndLen(start, end), metrics, guesstimates)

    dispatch({type: 'ADD_METRICS', items: newMetrics, newGuesstimates: newGuesstimates})

    dispatch(runSimulations({spaceId, metricSubset: newMetrics}))
  }
}
<commit_msg>No translatable inputs should preserve the original.<commit_after>import * as metricActions from 'gModules/metrics/actions'
import {runSimulations} from 'gModules/simulations/actions'

import e from 'gEngine/engine'

import {isAtLocation, isWithinRegion, getBounds, move, translate} from 'lib/locationUtils'

const DYNAMIC_FILL_TYPES = ['FUNCTION']

// TODO(matthew): Dry up code with this and copy and undo perhaps.
function translateReadableIds(input, idMap) {
  if (!input || _.isEmpty(idMap)) {return input}
  const re = RegExp(Object.keys(idMap).join("|"), "g")
  return input.replace(re, (match) => idMap[match])
}

const getDirAndLen = (start, end) => ({
  direction: {row: Math.sign(end.row - start.row), column: Math.sign(end.column - start.column)},
  length: Math.abs(start.row - end.row) || Math.abs(start.column - end.column),
})

function fillFunction(startMetric, startGuesstimate, metrics, idMap2, newLocation) {
  const metric = {...startMetric, ...e.metric.create(Object.keys(idMap2)), location: newLocation}
  const {input} = startGuesstimate
  const translateFn = translate(startMetric.location, newLocation)
  let idMap = {}
  metrics.forEach(m => {
    const matchedMetric = metrics.find(m2 => isAtLocation(translateFn(m.location), m2.location))
    if (!!matchedMetric) { idMap[m.readableId] = matchedMetric.readableId }
  })

  return { metric, guesstimate: {...startGuesstimate, metric: metric.id, input: translateReadableIds(input, idMap)} }
}

function addAtLoc(startMetric, startGuesstimate, metrics, idMap, newLocation) {
  const metric = {...startMetric, ...e.metric.create(Object.keys(idMap)), location: newLocation}
  return { metric, guesstimate: {...startGuesstimate, metric: metric.id} }
}

function buildNewMetrics(startMetric, startGuesstimate, startLocation, {direction, length}, metrics, guesstimates) {
  const {guesstimateType, input} = startGuesstimate

  let newMetrics = []
  let newGuesstimates = []

  const translateFn = DYNAMIC_FILL_TYPES.includes(guesstimateType) ?
    (loc, metrics, idMap) => fillFunction(startMetric, startGuesstimate, metrics, idMap, loc)
      :
    (loc, metrics, idMap) => addAtLoc(startMetric, startGuesstimate, metrics, idMap, loc)

  let idMap = {}
  metrics.forEach(m => {idMap[m.readableId] = m.id})

  let currLocation = move(startLocation, direction)
  for (var i = 0; i < length; i++) {
    const {metric, guesstimate} = translateFn(currLocation, metrics.concat(newMetrics), idMap)
    idMap[metric.readableId] = metric.id
    newMetrics.push(metric)
    newGuesstimates.push(guesstimate)
    currLocation = move(currLocation, direction)
  }

  return {newMetrics, newGuesstimates}
}

export function fillRegion(spaceId, {start, end}) {
  return (dispatch, getState) => {
    const state = getState()

    const metrics = state.metrics.filter(m => m.space === spaceId)
    const guesstimates = state.guesstimates.filter(g => _.some(metrics, m => m.id === g.metric))

    const startMetric = metrics.find(m => isAtLocation(m.location, start))

    const containedMetrics = metrics.filter(m => (!startMetric || m.id !== startMetric.id) && isWithinRegion(m.location, getBounds({start, end})))
    dispatch(metricActions.removeMetrics(containedMetrics.map(m => m.id)))

    if (!startMetric) { return }

    const startGuesstimate = guesstimates.find(g => g.metric === startMetric.id)

    const {newMetrics, newGuesstimates} = buildNewMetrics(startMetric, startGuesstimate, start, getDirAndLen(start, end), metrics, guesstimates)

    dispatch({type: 'ADD_METRICS', items: newMetrics, newGuesstimates: newGuesstimates})

    dispatch(runSimulations({spaceId, metricSubset: newMetrics}))
  }
}
<|endoftext|>


<commit_before>import { spawn } from 'child_process';
import fs from 'fs';
import path from 'path';
import createBus from 'resolve-bus';
import driverRabbitmq from 'resolve-bus-rabbitmq';
import driverZmq from 'resolve-bus-zmq';

import config from './config';

const POINTS = config.BENCHMARK_SERIES;

const busRabbitmq = createBus({ driver: driverRabbitmq({
    url: config.RABBITMQ_CONNECTION_URL
}) });

const busZmq = createBus({ driver: driverZmq({
    address: config.ZMQ_HOST,
    pubPort: config.ZMQ_PUB_PORT,
    subPort: config.ZMQ_SUB_PORT
}) });

function getRabbitmqHelper(eventCount) {
    return {
        spawner: spawn('babel-node', [path.join(__dirname, './readEventsRabbit'), eventCount]),
        emitter: event => busRabbitmq.emitEvent(event)
    };
}

function getZmqHelper(eventCount) {
    return {
        spawner: spawn('babel-node', [path.join(__dirname, './readEventsZmq'), eventCount]),
        emitter: event => busZmq.emitEvent(event)
    };
}

function generateEvent() {
    return {
        type: 'EVENT_TYPE',
        payload: {}
    };
}

function runLoadTests(helper) {
    const child = helper.launcher();
    const initialTime = +new Date();
    let consoleInfo = '';
    let isDone = false;

    let resolver;
    const done = new Promise((resolve, reject) => (resolver = resolve));

    const logHandler = rawData => (consoleInfo += rawData.toString('utf8'));

    const doneHandler = () => {
        const workTime = +new Date() - initialTime;
        const passedInfo = JSON.parse(consoleInfo);
        resolver({ ...passedInfo, time: workTime });
        isDone = true;
    };

    child.stdout.on('data', logHandler);
    child.stderr.on('data', logHandler);
    child.on('exit', doneHandler);

    const produceEventsAsync = () => {
        if (isDone) return;
        helper.emitter(generateEvent());
        setImmediate(produceEventsAsync);
    }

    produceEventsAsync();
    return done;
}

function fsWriteFileAsync(filename, content) {
    return new Promise((resolve, reject) =>
        fs.writeFile(filename, content, (err, value) => (
            !err ? resolve(value) : reject(err)
        ))
    );
}

function generateEvents(arr, i, storage) {
    const eventCount = arr[i];

    return runLoadTests(getRabbitmqHelper(eventCount))
        .then(rabbit => runLoadTests(getZmqHelper(eventCount))
            .then(zmq => (storage[eventCount] = { rabbit, zmq }))
        )
        .then(() => (i < arr.length - 1)
            ? generateEvents(arr, i + 1, storage)
            : storage
        );
}

generateEvents(POINTS, 0, {})
    .then(result => fsWriteFileAsync('./result.json', JSON.stringify(result)))
    .then(() => process.exit(0))
    .catch((err) => {
        // eslint-disable-next-line no-console
        console.log(err);
        process.exit(1);
    });
<commit_msg>Refactor rabbit-zmq load tests<commit_after>import { spawn } from 'child_process';
import fs from 'fs';
import path from 'path';
import createBus from 'resolve-bus';
import driverRabbitmq from 'resolve-bus-rabbitmq';
import driverZmq from 'resolve-bus-zmq';

import config from './config';

const POINTS = config.BENCHMARK_SERIES;

const busRabbitmq = createBus({ driver: driverRabbitmq({
    url: config.RABBITMQ_CONNECTION_URL
}) });

const busZmq = createBus({ driver: driverZmq({
    address: config.ZMQ_HOST,
    pubPort: config.ZMQ_PUB_PORT,
    subPort: config.ZMQ_SUB_PORT
}) });

function getRabbitmqHelper(eventCount) {
    return {
        launcher: () => spawn('babel-node', [
            path.join(__dirname, './readEventsRabbit'), eventCount
        ]),
        emitter: event => busRabbitmq.emitEvent(event)
    };
}

function getZmqHelper(eventCount) {
    return {
        launcher: () => spawn('babel-node', [
            path.join(__dirname, './readEventsZmq'), eventCount
        ]),
        emitter: event => busZmq.emitEvent(event)
    };
}

function generateEvent() {
    return {
        type: 'EVENT_TYPE',
        payload: {}
    };
}

function runLoadTests(helper) {
    const child = helper.launcher();
    const initialTime = +new Date();
    let consoleInfo = '';
    let isDone = false;

    let resolver;
    const done = new Promise(resolve => (resolver = resolve));

    const logHandler = rawData => (consoleInfo += rawData.toString('utf8'));

    const doneHandler = () => {
        const workTime = +new Date() - initialTime;
        const passedInfo = JSON.parse(consoleInfo);
        resolver({ ...passedInfo, time: workTime });
        isDone = true;
    };

    child.stdout.on('data', logHandler);
    child.stderr.on('data', logHandler);
    child.on('exit', doneHandler);

    const produceEventsAsync = () => {
        if (isDone) return;
        helper.emitter(generateEvent());
        setImmediate(produceEventsAsync);
    }

    produceEventsAsync();
    return done;
}

function fsWriteFileAsync(filename, content) {
    return new Promise((resolve, reject) =>
        fs.writeFile(filename, content, (err, value) => (
            !err ? resolve(value) : reject(err)
        ))
    );
}

function generateEvents(arr, i, storage) {
    const eventCount = arr[i];

    return runLoadTests(getRabbitmqHelper(eventCount))
        .then(rabbit => runLoadTests(getZmqHelper(eventCount))
            .then(zmq => (storage[eventCount] = { rabbit, zmq }))
        )
        .then(() => (i < arr.length - 1)
            ? generateEvents(arr, i + 1, storage)
            : storage
        );
}

generateEvents(POINTS, 0, {})
    .then(result => fsWriteFileAsync('./result.json', JSON.stringify(result)))
    .then(() => process.exit(0))
    .catch((err) => {
        // eslint-disable-next-line no-console
        console.log(err);
        process.exit(1);
    });
<|endoftext|>


<commit_before>// adt.js
// ------
// Algebraic data types and immutable structures in Javascript
//
// version : 0.5.0
// author  : Nathan Faubion <nathan@n-son.com>
// license : MIT

;(function (window, module) {
  // adt namespace
  var adt = {};

  // Export
  window.adt = module.exports = adt;

  // Utility functions
  adt.util = {};

  // Cache some prototype methods for easy use.
  var slice = Array.prototype.slice;
  var funcApply = Function.prototype.apply;

  // Return an array of the supplied argument. Used for `arguments` objects.
  adt.util.toArray = function (a) {
    var dest = [], i = 0, len = a.length;
    for (; i < len; i++) dest[i] = a[i];
    return dest;
  };

  // A basic extend method for copying attributes of on object into another.
  adt.util.extend = function (dest /*, sources... */) {
    var args = slice.call(arguments, 1);
    for (var i = 0, len = args.length; i < len; i++) {
      for (var k in args[i]) {
        if (args[i].hasOwnProperty(k)) dest[k] = args[i][k];
      }
    }
    return dest;
  };

  // Given a constructor and an array of args, instanciates a new objects with
  // said args. Like calling `apply` on a constructor.
  adt.util.ctrApply = function (ctr, args) {
    var C = function () {};
    C.prototype = ctr.prototype;
    var inst = new C();
    var ret = ctr.apply(inst, args);
    return Object(ret) === ret ? ret : inst;
  };

  // Partially applies arguments to a function in the same way `bind` would,
  // but doesn't cement the context in anyway.
  adt.util.partial = function (func) {
    var args = slice.call(arguments, 1);
    return function () {
      return func.apply(this, args.concat(adt.util.toArray(arguments)));
    };
  };

  // Standard inheritance helper.
  adt.util.inherit = function (sup, sub) {
    var C = function () {};
    C.prototype = sup.prototype;
    sub.prototype = new C();
    sub.prototype.constructor = sub;
    return sub;
  };

  // Base class from which all adt.js classes inherit.
  // All adt.js classes should have a clone method that clones the
  // object as best it can.
  adt.__Base__ = function () {};

  // Generates a new set of Algebraic data types. This will create a lot of
  // boilerplate to help with type checking. Each type will get is<type>()
  // methods that return true or false.
  adt.data = function () {
    var targ0 = typeof arguments[0];
    var callback, types, names;

    // adt.data(typesObj)
    if (targ0 === 'object') {
      types = arguments[0];

      // Call adt.data again with a desugared callback.
      return adt.data(function (d, type) {
        for (var name in types) type(name, types[name]);
      });
    }

    // adt.data(callback)
    callback = arguments[0] || function () {};
    names = [];

    // Create a new parent class.
    // This class should never be created using `new`. You obviously can,
    // but it won't be of much use. You can however override the apply method
    // to create default instances.
    var D = adt.util.inherit(adt.__Base__, function () {
      if (!(this instanceof D) && D.apply !== funcApply) {
        return D.apply(this, arguments);
      }
    });

    // Declares an adt type as part of the family.
    D.type = function (name, tmpl) {
      if (typeof name !== 'string') {
        tmpl = name;
        name = uniqueId('Anonymous');
      }
      // Create a new adt template if not provided with one
      if (!tmpl) tmpl = adt.single();
      else if (typeof tmpl !== 'function') tmpl = adt.record(tmpl);

      // Add typechecking attributes for this type.
      D.prototype['is' + name] = false;

      // Call the template to build our type and export it on the parent type.
      D[name] = tmpl(D, name);
      names.push(name);
      return D[name];
    };

    // Call the callback with the constructor as the context.
    var types = callback.call(D, D, D.type);

    // If an object was returned in the callback, assume it's a mapping of
    // more types to add.
    if (typeof types === 'object' && !(types instanceof adt.__Base__)) {
      for (var name in types) {
        if (types.hasOwnProperty(name)) D.type(name, types[name]);
      }
    }

    // Keep the type function around because it allows for nice type
    // declarations, but give the option to seal it. This will call `seal`
    // on any sub types to.
    D.seal = function () {
      var i = 0, len = names.length, seal;
      for (; i < len; i++) {
        seal = this[name].seal;
        seal instanceof Function && seal();
      }
      delete D.type;
      delete D.seal;
      return D;
    };

    // Export names as a meta object
    D.__names__ = names;
    return D;
  };

  // Create a single empty class that always return the same instance.
  // Useful for sentinal values such as Nothing, Empty, etc.
  adt.single = function () {
    var ctr, inst;
    ctr = function () {
      if (!(this instanceof ctr)) return new ctr();
      if (inst) return inst;
      inst = this;
    };

    return function (parent, name) {
      adt.util.inherit(parent, ctr);
      ctr.className = name;
      ctr.prototype.clone = function () { return inst; };
      ctr.prototype.equals = function (obj) { return this === obj; };
      ctr.prototype.toString = function () { return name; };
      ctr.prototype['is' + name] = true;
      return new ctr();
    };
  };

  // Create a new class that has named fields. Each value can be obtained by
  // calling the name as a method. Each value can be changed by calling `set`
  // with an object of values to update. `set` returns a clone of the object.
  adt.record = function () {
    var targ0 = typeof arguments[0];
    var names, constraints, ctr, fields, callback;

    // adt.record(fieldNames...)
    if (targ0 === 'string') {
      var args = adt.util.toArray(arguments);
      return adt.record(function (r, field) {
        var i = 0, len = args.length;
        for (; i < len; i++) field(args[i], adt.any);
      });
    }

    // adt.record(fieldsObj)
    else if (targ0 === 'object') {
      fields = arguments[0];
      return adt.record(function (r, field) {
        for (var name in fields) field(name, fields[name]);
      });
    }

    // adt.record(callback)
    callback = arguments[0] || function () {};
    names = [];
    constraints = {};

    // A record's constructor can be called without `new` and will also throw
    // an error if called with too many arguments. Its arguments can be curried
    // as long as it isn't called with the `new` keyword.
    ctr = function () {
      var args = adt.util.toArray(arguments);
      var len = names.length;
      if (!(this instanceof ctr)) {
        return args.length < len
          ? adt.util.partial.apply(null, [ctr].concat(args))
          : adt.util.ctrApply(ctr, args);
      } else {
        if (args.length < len) throw new Error('Too few arguments');
        if (args.length > len) throw new Error('Too many arguments');
        var i = 0, len = args.length, n;
        for (; i < len; i++) {
          n = names[i];
          this[n] = constraints[n](args[i]);
        }
      }
    };

    return function (parent, name) {
      adt.util.inherit(parent, ctr);
      ctr.className = name;
      ctr.prototype['is' + name] = true;

      // Custom `toString` implementation.
      ctr.prototype.toString = function () {
        var vals = ctr.unapply(this);
        return name + (vals.length ? '(' + vals.join(', ') + ')' : '');
      };

      // This clone method will clone any values that are also adt types and
      // leaves anything else alone.
      ctr.prototype.clone = function () {
        var args = [];
        var i = 0, len = names.length, val;
        for (; i < len; i++) {
          val = this[names[i]];
          args.push(val instanceof adt.__Base__ ? val.clone() : val);
        }
        return ctr.apply(null, args);
      };

      // Lookup fields by either name or index. Throws an error if the name
      // doesn't exist or if the index is out of range.
      ctr.prototype.get = function (field) {
        if (typeof field === 'number') {
          if (field < 0 || field > names.length - 1) {
            throw new Error('Field index out of range');
          }
          field = names[field];
        } else {
          if (!constraints.hasOwnProperty(field)) {
            throw new Error('Field name does not exist');
          }
        }
        return this[field];
      };

      // Creates a new instance with the specified values changed.
      ctr.prototype.set = function (vals) {
        var args = []
        var i = 0, len = names.length, val, n;
        for (; i < len; i++) {
          n = names[i];
          val = n in vals ? vals[n] : this[n];
          args.push(val);
        }
        return ctr.apply(null, args);
      };

      // Performs deep equality checks on each field as long as it holds an
      // adt type. Any other types will just be compared using ===.
      ctr.prototype.equals = function (that) {
        if (this === that) return true;
        if (!(that instanceof ctr)) return false;
        var i = 0, len = names.length;
        var vala, valb, n;
        for (; i < len; i++) {
          n = names[i];
          vala = this[n];
          valb = that[n];
          if (vala instanceof adt.__Base__) {
            if (!vala.equals(valb)) return false;
          } else if (vala !== valb) return false;
        }
        return true;
      };

      // Creates a new instance using key-value pairs instead of by
      // positional arguments.
      ctr.create = function (vals) {
        var args = [];
        var i = 0, len = names.length, n;
        for (; i < len; i++) {
          n = names[i];
          if (!(n in vals)) throw new Error('Could not find field in arguments: ' + n);
          args.push(vals[n]);
        }
        return ctr.apply(null, args);
      };

      // Returns an array representation of the fields.
      ctr.unapply = function (inst) {
        var vals = [];
        var i = 0, len = names.length;
        for (; i < len; i++) vals.push(inst[names[i]]);
        return vals;
      };

      // Returns an object representation of the field.
      ctr.unapplyObj = function (inst) {
        var vals = {};
        var i = 0, len = names.length;
        for (; i < len; i++) vals[names[i]] = inst[names[i]];
        return vals;
      };

      // Declares a field as part of the type.
      ctr.field = function (name, constraint) {
        if (!constraint) constraint = adt.any;
        if (typeof constraint !== 'function') {
          throw new TypeError('Constraints must be functions')
        }
        names.push(name);
        constraints[name] = constraint;
        return ctr;
      };

      // Call the callback with the contructor as the context.
      fields = callback.call(ctr, ctr, ctr.field);

      // If an object was returned in the callback, assume it's a mapping of
      // more fields to add.
      if (typeof fields === 'object' && fields !== ctr) {
        for (var name in fields) {
          if (!fields.hasOwnProperty(name)) continue;
          ctr.field(name, fields[name]);
        }
      }

      // Export names and constraints as meta attributes.
      ctr.__names__ = names;
      ctr.__constraints__ = constraints;

      // Keep the field function around because it allows for nice type
      // declarations, but give the option to seal it.
      ctr.seal = function () {
        delete ctr.field;
        delete ctr.seal;
        return ctr;
      };

      return ctr;
    };
  };

  // Enumerations are types that have an order and can be compared using lt,
  // gt, lte, and gte.
  adt.enumeration = function () {
    var targ0 = typeof arguments[0];

    // adt.enumeration(typeNames...)
    if (targ0 === 'string') {
      var args = adt.util.toArray(arguments);
      return adt.enumeration(function (r, type) {
        var i = 0, len = args.length;
        for (; i < len; i++) type(args[i]);
      });
    }

    // Create the types
    var E = adt.data.apply(null, arguments);

    // Iterate through created types, applying an order meta attribute.
    var i = 0, len = E.__names__.length, name;
    for (; i < len; i++) {
      ctr = E[E.__names__[i]];
      if (ctr.constructor) ctr = ctr.constructor;
      ctr.__order__ = i;
    }

    // Helper function to make sure we are comparing the same types. We can't
    // compare the order of a different type, so it throws a TypeError if the
    // types are incompatible.
    function verifyType (that) {
      if (!(that instanceof E)) throw new TypeError('Unexpected type');
      return true;
    }

    // Helper to return the order of the particular instance
    function order (that) {
      return that.constructor.__order__;
    }

    // Less than
    E.prototype.lt = function (that) {
      return verifyType(that) && order(this) < order(that);
    };

    // Less than or equal
    E.prototype.lte = function (that) {
      return verifyType(that) && order(this) <= order(that);
    };

    // Greater than
    E.prototype.gt = function (that) {
      return verifyType(that) && order(this) > order(that);
    };

    // Greater than or equal
    E.prototype.gte = function (that) {
      return verifyType(that) && order(this) >= order(that);
    };

    // Equal (not that same as `equals`
    E.prototype.eq = function (that) {
      return verifyType(that) && order(this) === order(that);
    };

    // Not equals
    E.prototype.neq = function (that) {
      return verifyType(that) && order(this) !== order(that);
    };

    return E;
  };

  // Creates a singleton type that belongs to its own family.
  adt.newtype = function () {
    var args = adt.util.toArray(arguments);
    var data = adt.data();
    return data.type.apply(data, args);
  };

  // A contraint function that will accept any value.
  adt.any = function (x) { return x; };

  // A constraint generator that will perform instanceof checks on the value
  // to make sure it is of the correct type. If a value besides a function
  // is passed in, it will perform equality checks on it.
  //
  // It has special handling for Number, String, and Boolean. Literals won't
  // match an instanceof check, but will match with typeof.
  adt.only = function () {
    var args = arguments;
    return function (x) {
      var i = 0, len = args.length, type;
      for (; i < len; i++) {
        type = args[i];
        if (type instanceof Function) {
          if (x instanceof type
          || type === Number && typeof x === 'number'
          || type === String && typeof x === 'string'
          || type === Boolean && typeof x === 'boolean') return x;
        } else {
          if (type instanceof adt.__Base__ && type.equals(x)
          || type === x) return x;
        }
      }
      throw new TypeError('Unexpected type');
    };
  };

  // Helper function to return a unique id
  var uniqueId = (function () {
    var id = 0;
    return function (pre) { return pre + (id++); };
  })();

})(
  typeof window !== 'undefined' ? window : {},
  typeof module !== 'undefined' ? module : {}
);
<commit_msg>Move uniqueId to the adt.util namespace<commit_after>// adt.js
// ------
// Algebraic data types and immutable structures in Javascript
//
// version : 0.5.0
// author  : Nathan Faubion <nathan@n-son.com>
// license : MIT

;(function (window, module) {
  // adt namespace
  var adt = {};

  // Export
  window.adt = module.exports = adt;

  // Utility functions
  adt.util = {};

  // Cache some prototype methods for easy use.
  var slice = Array.prototype.slice;
  var funcApply = Function.prototype.apply;

  // Return an array of the supplied argument. Used for `arguments` objects.
  adt.util.toArray = function (a) {
    var dest = [], i = 0, len = a.length;
    for (; i < len; i++) dest[i] = a[i];
    return dest;
  };

  // A basic extend method for copying attributes of on object into another.
  adt.util.extend = function (dest /*, sources... */) {
    var args = slice.call(arguments, 1);
    for (var i = 0, len = args.length; i < len; i++) {
      for (var k in args[i]) {
        if (args[i].hasOwnProperty(k)) dest[k] = args[i][k];
      }
    }
    return dest;
  };

  // Given a constructor and an array of args, instanciates a new objects with
  // said args. Like calling `apply` on a constructor.
  adt.util.ctrApply = function (ctr, args) {
    var C = function () {};
    C.prototype = ctr.prototype;
    var inst = new C();
    var ret = ctr.apply(inst, args);
    return Object(ret) === ret ? ret : inst;
  };

  // Partially applies arguments to a function in the same way `bind` would,
  // but doesn't cement the context in anyway.
  adt.util.partial = function (func) {
    var args = slice.call(arguments, 1);
    return function () {
      return func.apply(this, args.concat(adt.util.toArray(arguments)));
    };
  };

  // Standard inheritance helper.
  adt.util.inherit = function (sup, sub) {
    var C = function () {};
    C.prototype = sup.prototype;
    sub.prototype = new C();
    sub.prototype.constructor = sub;
    return sub;
  };

  // Helper function to return a unique id
  adt.util.uniqueId = (function () {
    var id = 0;
    return function (pre) { return pre + (id++); };
  })();

  // Base class from which all adt.js classes inherit.
  // All adt.js classes should have a clone method that clones the
  // object as best it can.
  adt.__Base__ = function () {};

  // Generates a new set of Algebraic data types. This will create a lot of
  // boilerplate to help with type checking. Each type will get is<type>()
  // methods that return true or false.
  adt.data = function () {
    var targ0 = typeof arguments[0];
    var callback, types, names;

    // adt.data(typesObj)
    if (targ0 === 'object') {
      types = arguments[0];

      // Call adt.data again with a desugared callback.
      return adt.data(function (d, type) {
        for (var name in types) type(name, types[name]);
      });
    }

    // adt.data(callback)
    callback = arguments[0] || function () {};
    names = [];

    // Create a new parent class.
    // This class should never be created using `new`. You obviously can,
    // but it won't be of much use. You can however override the apply method
    // to create default instances.
    var D = adt.util.inherit(adt.__Base__, function () {
      if (!(this instanceof D) && D.apply !== funcApply) {
        return D.apply(this, arguments);
      }
    });

    // Declares an adt type as part of the family.
    D.type = function (name, tmpl) {
      if (typeof name !== 'string') {
        tmpl = name;
        name = adt.util.uniqueId('Anonymous');
      }
      // Create a new adt template if not provided with one
      if (!tmpl) tmpl = adt.single();
      else if (typeof tmpl !== 'function') tmpl = adt.record(tmpl);

      // Add typechecking attributes for this type.
      D.prototype['is' + name] = false;

      // Call the template to build our type and export it on the parent type.
      D[name] = tmpl(D, name);
      names.push(name);
      return D[name];
    };

    // Call the callback with the constructor as the context.
    var types = callback.call(D, D, D.type);

    // If an object was returned in the callback, assume it's a mapping of
    // more types to add.
    if (typeof types === 'object' && !(types instanceof adt.__Base__)) {
      for (var name in types) {
        if (types.hasOwnProperty(name)) D.type(name, types[name]);
      }
    }

    // Keep the type function around because it allows for nice type
    // declarations, but give the option to seal it. This will call `seal`
    // on any sub types to.
    D.seal = function () {
      var i = 0, len = names.length, seal;
      for (; i < len; i++) {
        seal = this[name].seal;
        seal instanceof Function && seal();
      }
      delete D.type;
      delete D.seal;
      return D;
    };

    // Export names as a meta object
    D.__names__ = names;
    return D;
  };

  // Create a single empty class that always return the same instance.
  // Useful for sentinal values such as Nothing, Empty, etc.
  adt.single = function () {
    var ctr, inst;
    ctr = function () {
      if (!(this instanceof ctr)) return new ctr();
      if (inst) return inst;
      inst = this;
    };

    return function (parent, name) {
      adt.util.inherit(parent, ctr);
      ctr.className = name;
      ctr.prototype.clone = function () { return inst; };
      ctr.prototype.equals = function (obj) { return this === obj; };
      ctr.prototype.toString = function () { return name; };
      ctr.prototype['is' + name] = true;
      return new ctr();
    };
  };

  // Create a new class that has named fields. Each value can be obtained by
  // calling the name as a method. Each value can be changed by calling `set`
  // with an object of values to update. `set` returns a clone of the object.
  adt.record = function () {
    var targ0 = typeof arguments[0];
    var names, constraints, ctr, fields, callback;

    // adt.record(fieldNames...)
    if (targ0 === 'string') {
      var args = adt.util.toArray(arguments);
      return adt.record(function (r, field) {
        var i = 0, len = args.length;
        for (; i < len; i++) field(args[i], adt.any);
      });
    }

    // adt.record(fieldsObj)
    else if (targ0 === 'object') {
      fields = arguments[0];
      return adt.record(function (r, field) {
        for (var name in fields) field(name, fields[name]);
      });
    }

    // adt.record(callback)
    callback = arguments[0] || function () {};
    names = [];
    constraints = {};

    // A record's constructor can be called without `new` and will also throw
    // an error if called with too many arguments. Its arguments can be curried
    // as long as it isn't called with the `new` keyword.
    ctr = function () {
      var args = adt.util.toArray(arguments);
      var len = names.length;
      if (!(this instanceof ctr)) {
        return args.length < len
          ? adt.util.partial.apply(null, [ctr].concat(args))
          : adt.util.ctrApply(ctr, args);
      } else {
        if (args.length < len) throw new Error('Too few arguments');
        if (args.length > len) throw new Error('Too many arguments');
        var i = 0, len = args.length, n;
        for (; i < len; i++) {
          n = names[i];
          this[n] = constraints[n](args[i]);
        }
      }
    };

    return function (parent, name) {
      adt.util.inherit(parent, ctr);
      ctr.className = name;
      ctr.prototype['is' + name] = true;

      // Custom `toString` implementation.
      ctr.prototype.toString = function () {
        var vals = ctr.unapply(this);
        return name + (vals.length ? '(' + vals.join(', ') + ')' : '');
      };

      // This clone method will clone any values that are also adt types and
      // leaves anything else alone.
      ctr.prototype.clone = function () {
        var args = [];
        var i = 0, len = names.length, val;
        for (; i < len; i++) {
          val = this[names[i]];
          args.push(val instanceof adt.__Base__ ? val.clone() : val);
        }
        return ctr.apply(null, args);
      };

      // Lookup fields by either name or index. Throws an error if the name
      // doesn't exist or if the index is out of range.
      ctr.prototype.get = function (field) {
        if (typeof field === 'number') {
          if (field < 0 || field > names.length - 1) {
            throw new Error('Field index out of range');
          }
          field = names[field];
        } else {
          if (!constraints.hasOwnProperty(field)) {
            throw new Error('Field name does not exist');
          }
        }
        return this[field];
      };

      // Creates a new instance with the specified values changed.
      ctr.prototype.set = function (vals) {
        var args = []
        var i = 0, len = names.length, val, n;
        for (; i < len; i++) {
          n = names[i];
          val = n in vals ? vals[n] : this[n];
          args.push(val);
        }
        return ctr.apply(null, args);
      };

      // Performs deep equality checks on each field as long as it holds an
      // adt type. Any other types will just be compared using ===.
      ctr.prototype.equals = function (that) {
        if (this === that) return true;
        if (!(that instanceof ctr)) return false;
        var i = 0, len = names.length;
        var vala, valb, n;
        for (; i < len; i++) {
          n = names[i];
          vala = this[n];
          valb = that[n];
          if (vala instanceof adt.__Base__) {
            if (!vala.equals(valb)) return false;
          } else if (vala !== valb) return false;
        }
        return true;
      };

      // Creates a new instance using key-value pairs instead of by
      // positional arguments.
      ctr.create = function (vals) {
        var args = [];
        var i = 0, len = names.length, n;
        for (; i < len; i++) {
          n = names[i];
          if (!(n in vals)) throw new Error('Could not find field in arguments: ' + n);
          args.push(vals[n]);
        }
        return ctr.apply(null, args);
      };

      // Returns an array representation of the fields.
      ctr.unapply = function (inst) {
        var vals = [];
        var i = 0, len = names.length;
        for (; i < len; i++) vals.push(inst[names[i]]);
        return vals;
      };

      // Returns an object representation of the field.
      ctr.unapplyObj = function (inst) {
        var vals = {};
        var i = 0, len = names.length;
        for (; i < len; i++) vals[names[i]] = inst[names[i]];
        return vals;
      };

      // Declares a field as part of the type.
      ctr.field = function (name, constraint) {
        if (!constraint) constraint = adt.any;
        if (typeof constraint !== 'function') {
          throw new TypeError('Constraints must be functions')
        }
        names.push(name);
        constraints[name] = constraint;
        return ctr;
      };

      // Call the callback with the contructor as the context.
      fields = callback.call(ctr, ctr, ctr.field);

      // If an object was returned in the callback, assume it's a mapping of
      // more fields to add.
      if (typeof fields === 'object' && fields !== ctr) {
        for (var name in fields) {
          if (!fields.hasOwnProperty(name)) continue;
          ctr.field(name, fields[name]);
        }
      }

      // Export names and constraints as meta attributes.
      ctr.__names__ = names;
      ctr.__constraints__ = constraints;

      // Keep the field function around because it allows for nice type
      // declarations, but give the option to seal it.
      ctr.seal = function () {
        delete ctr.field;
        delete ctr.seal;
        return ctr;
      };

      return ctr;
    };
  };

  // Enumerations are types that have an order and can be compared using lt,
  // gt, lte, and gte.
  adt.enumeration = function () {
    var targ0 = typeof arguments[0];

    // adt.enumeration(typeNames...)
    if (targ0 === 'string') {
      var args = adt.util.toArray(arguments);
      return adt.enumeration(function (r, type) {
        var i = 0, len = args.length;
        for (; i < len; i++) type(args[i]);
      });
    }

    // Create the types
    var E = adt.data.apply(null, arguments);

    // Iterate through created types, applying an order meta attribute.
    var i = 0, len = E.__names__.length, name;
    for (; i < len; i++) {
      ctr = E[E.__names__[i]];
      if (ctr.constructor) ctr = ctr.constructor;
      ctr.__order__ = i;
    }

    // Helper function to make sure we are comparing the same types. We can't
    // compare the order of a different type, so it throws a TypeError if the
    // types are incompatible.
    function verifyType (that) {
      if (!(that instanceof E)) throw new TypeError('Unexpected type');
      return true;
    }

    // Helper to return the order of the particular instance
    function order (that) {
      return that.constructor.__order__;
    }

    // Less than
    E.prototype.lt = function (that) {
      return verifyType(that) && order(this) < order(that);
    };

    // Less than or equal
    E.prototype.lte = function (that) {
      return verifyType(that) && order(this) <= order(that);
    };

    // Greater than
    E.prototype.gt = function (that) {
      return verifyType(that) && order(this) > order(that);
    };

    // Greater than or equal
    E.prototype.gte = function (that) {
      return verifyType(that) && order(this) >= order(that);
    };

    // Equal (not that same as `equals`
    E.prototype.eq = function (that) {
      return verifyType(that) && order(this) === order(that);
    };

    // Not equals
    E.prototype.neq = function (that) {
      return verifyType(that) && order(this) !== order(that);
    };

    return E;
  };

  // Creates a singleton type that belongs to its own family.
  adt.newtype = function () {
    var args = adt.util.toArray(arguments);
    var data = adt.data();
    return data.type.apply(data, args);
  };

  // A contraint function that will accept any value.
  adt.any = function (x) { return x; };

  // A constraint generator that will perform instanceof checks on the value
  // to make sure it is of the correct type. If a value besides a function
  // is passed in, it will perform equality checks on it.
  //
  // It has special handling for Number, String, and Boolean. Literals won't
  // match an instanceof check, but will match with typeof.
  adt.only = function () {
    var args = arguments;
    return function (x) {
      var i = 0, len = args.length, type;
      for (; i < len; i++) {
        type = args[i];
        if (type instanceof Function) {
          if (x instanceof type
          || type === Number && typeof x === 'number'
          || type === String && typeof x === 'string'
          || type === Boolean && typeof x === 'boolean') return x;
        } else {
          if (type instanceof adt.__Base__ && type.equals(x)
          || type === x) return x;
        }
      }
      throw new TypeError('Unexpected type');
    };
  };

})(
  typeof window !== 'undefined' ? window : {},
  typeof module !== 'undefined' ? module : {}
);
<|endoftext|>


<commit_before>const LyricBase = require('../include/lyric-base');
const BlockedError = require('../include/blocked-error');

const keyword = 'azlyrics';

class Lyric extends LyricBase {
  find_lyric(url, html) {
    const prefix = '


<commit_before>var pg = require('pg');
var _ = require('underscore');
var jwt = require('jsonwebtoken');
var secret = require('./../config/secret');
var db_settings = require('../server.js').db_settings;
var errors = require('./../config/errors');

var Ajv = require('ajv');
var schema = require('./../models/query');
var ajv = Ajv({"format": "full"});
var validate = ajv.compile(schema);

var CouchDB_Client = require('../controllers/connectors/CouchDB_Client.js');
var EnviroCar_Client = require('../controllers/connectors/EnviroCar_Client.js');
var Postgres_Client = require('../controllers/connectors/Postgres_Client.js');
var Sparql_Client = require('../controllers/connectors/Sparql_Client.js');

// POST
exports.request = function(req, res){

    // Create timestamp
    var time = Date.now();

    // Check if Header contains Access-Token
    if(!req.headers.authorization || req.headers.authorization === ""){
        res.status(errors.authentication.error_2.code).send(errors.authentication.error_2);
        return console.error(errors.authentication.error_2.message);
    } else {

        // Verify Access-Token
        jwt.verify(req.headers.authorization, secret.key, function(err, decoded) {
            if (err) {
                res.status(errors.authentication.error_1.code).send(errors.authentication.error_1);
                return console.error(errors.authentication.error_1.message);
            } else {
                var url = "postgres://" + db_settings.user + ":" + db_settings.password + "@" + db_settings.host + ":" + db_settings.port + "/" + db_settings.database_name;

                pg.connect(url, function(err, client, done) {
                    if (err) {
                        res.status(errors.database.error_1.code).send(errors.database.error_1);
                        return console.error(errors.database.error_1.message, err);
                    } else {

                        // Database Query
                        client.query('SELECT * FROM Apps WHERE app_name=$1;', [
                            decoded.app_name
                        ], function(err, result) {
                            done();

                            if (err) {
                                res.status(errors.database.error_2.code).send(_.extend(errors.database.error_2, err));
                                return console.error(errors.database.error_2.message, err);
                            } else {

                                // Check if App exists
                                if (result.rows.length === 0) {
                                    res.status(errors.query.error_1.code).send(errors.query.error_1);
                                    console.error(errors.query.error_1.message);
                                } else {

                                    // Prepare result
                                    var app = result.rows[0];
                                    client.query('INSERT INTO Logs VALUES($1, now());', [
                                        decoded.app_name
                                    ], function(err, result) {
                                        /* if (err) {
                                            res.status(errors.database.error_2.code).send(_.extend(errors.database.error_2, err));
                                            return console.error(errors.database.error_2.message, err);
                                        }*/
                                    });

                                    var Answer = {
                                        enviroCar: {
                                            time: 0,
                                            count: 0,
                                            data: []
                                        },
                                        postgres: {
                                            time: 0,
                                            count: 0,
                                            data: []
                                        },
                                        parliament: {
                                            time: 0,
                                            count: 0,
                                            data: []
                                        },
                                        couchDB: {
                                            time: 0,
                                            count: 0,
                                            data: []
                                        }
                                    };
                                    var answerCount = 0;
                                    //Envirocar
                                    var enviroCar_Client = new EnviroCar_Client();
                                    enviroCar_Client.query("sensors", function(data) {
                                        Answer.enviroCar.time = (Date.now() - time) / 1000 + " s";
                                        Answer.enviroCar.data = data.results;
                                        answerCount += 1;
                                        finish(res, Answer, answerCount, 1);
                                    });
                                    //postgres
                                    var postgres_Client = new Postgres_Client(url);
                                    //postgres_Client.setURL(url);
                                    postgres_Client.query('SELECT logs."timestamp", logs.app_name FROM public.logs;', [], function(result) {
                                        Answer.postgres.time = (Date.now() - time) / 1000 + " s";
                                        Answer.postgres.data = result.parseRowsByColNames("Datasets").Datasets;
                                        answerCount += 1;
                                        finish(res, Answer, answerCount, 1);
                                    });
                                    //parliament
                                    var sparql_Client = new Sparql_Client();
                                    sparql_Client.query("SELECT ?p ?o { <http://vocab.lodcom.de/muenster> ?p ?o }", function(result) {
                                        Answer.parliament.time = (Date.now() - time) / 1000 + " s";
                                        Answer.parliament.data = result;
                                        answerCount += 1;
                                        finish(res, Answer, answerCount, 1);
                                    });
                                    //CouchDB
                                    var couchDB_Client = new CouchDB_Client();
                                    couchDB_Client.query(function (result) {
                                        Answer.couchDB.time = (Date.now()-time) / 1000 + " s";
                                        Answer.couchDB.data = result;
                                        answerCount += 1;
                                        finish(res, Answer, answerCount, 1);
                                    });
                                }
                            }
                        });
                    }
                });
            }
        });
    }
};

/**
 * Check
 * @param  {number} count
 * @param  {number} maximum
 * @return {boolean} true or false
 */
var check = function (count, max) {
    max = max*4;
    if(count == max) {
        return true;
    } else {
        return false;
    }
};

/**
 * Finish
 */
var finish = function (res, Answer, answerCount, max) {
    if(check(answerCount, max)) {
        Answer.enviroCar.count = Answer.enviroCar.data.length;
        Answer.parliament.count = Answer.parliament.data.length;
        Answer.postgres.count = Answer.postgres.data.length;
        Answer.couchDB.count = Answer.couchDB.data.length;
        res.status(201).send(Answer);
    }
};
<commit_msg>Code-Cleaning<commit_after>var pg = require('pg');
var _ = require('underscore');
var jwt = require('jsonwebtoken');
var secret = require('./../config/secret');
var db_settings = require('../server.js').db_settings;
var errors = require('./../config/errors');

var Ajv = require('ajv');
var schema = require('./../models/query');
var ajv = Ajv({"format": "full"});
var validate = ajv.compile(schema);

var CouchDB_Client = require('../controllers/connectors/CouchDB_Client.js');
var EnviroCar_Client = require('../controllers/connectors/EnviroCar_Client.js');
var Postgres_Client = require('../controllers/connectors/Postgres_Client.js');
var Sparql_Client = require('../controllers/connectors/Sparql_Client.js');

// POST
exports.request = function(req, res){

    // Create timestamp
    var time = Date.now();

    // Check if Header contains Access-Token
    if(!req.headers.authorization || req.headers.authorization === ""){
        res.status(errors.authentication.error_2.code).send(errors.authentication.error_2);
        return console.error(errors.authentication.error_2.message);
    } else {

        // Verify Access-Token
        jwt.verify(req.headers.authorization, secret.key, function(err, decoded) {
            if (err) {
                res.status(errors.authentication.error_1.code).send(errors.authentication.error_1);
                return console.error(errors.authentication.error_1.message);
            } else {

                var url = "postgres://" + db_settings.user + ":" + db_settings.password + "@" + db_settings.host + ":" + db_settings.port + "/" + db_settings.database_name;

                // Connect to Database
                pg.connect(url, function(err, client, done) {
                    if (err) {
                        res.status(errors.database.error_1.code).send(errors.database.error_1);
                        return console.error(errors.database.error_1.message, err);
                    } else {

                        // Database Query
                        client.query('SELECT * FROM Apps WHERE app_name=$1;', [
                            decoded.app_name
                        ], function(err, result) {
                            done();

                            if (err) {
                                res.status(errors.database.error_2.code).send(_.extend(errors.database.error_2, err));
                                return console.error(errors.database.error_2.message, err);
                            } else {

                                // Check if App exists
                                if (result.rows.length === 0) {
                                    res.status(errors.query.error_1.code).send(errors.query.error_1);
                                    console.error(errors.query.error_1.message);
                                } else {

                                    // Prepare Result
                                    var app = result.rows[0];

                                    // Logging
                                    client.query('INSERT INTO Logs VALUES($1, now());', [
                                        decoded.app_name
                                    ], function(err, result) {
                                        if (err) {
                                            res.status(errors.database.error_2.code).send(_.extend(errors.database.error_2, err));
                                            return console.error(errors.database.error_2.message, err);
                                        } else {

                                            // Prepare Connectors
                                            var Answer = {
                                                enviroCar: {
                                                    time: 0,
                                                    count: 0,
                                                    data: []
                                                },
                                                postgres: {
                                                    time: 0,
                                                    count: 0,
                                                    data: []
                                                },
                                                parliament: {
                                                    time: 0,
                                                    count: 0,
                                                    data: []
                                                },
                                                couchDB: {
                                                    time: 0,
                                                    count: 0,
                                                    data: []
                                                }
                                            };
                                            var answerCount = 0;

                                            // EnviroCar
                                            var enviroCar_Client = new EnviroCar_Client();
                                            enviroCar_Client.query("sensors", function(data) {
                                                Answer.enviroCar.time = (Date.now() - time) / 1000 + " s";
                                                Answer.enviroCar.data = data.results;
                                                answerCount += 1;
                                                finish(res, Answer, answerCount, 1);
                                            });

                                            // PostgreSQL
                                            var postgres_Client = new Postgres_Client(url);
                                            // postgres_Client.setURL(url);
                                            postgres_Client.query('SELECT logs."timestamp", logs.app_name FROM public.logs;', [], function(result) {
                                                Answer.postgres.time = (Date.now() - time) / 1000 + " s";
                                                Answer.postgres.data = result.parseRowsByColNames("Datasets").Datasets;
                                                answerCount += 1;
                                                finish(res, Answer, answerCount, 1);
                                            });

                                            // Parliament
                                            var sparql_Client = new Sparql_Client();
                                            sparql_Client.query("SELECT ?p ?o { <http://vocab.lodcom.de/muenster> ?p ?o }", function(result) {
                                                Answer.parliament.time = (Date.now() - time) / 1000 + " s";
                                                Answer.parliament.data = result;
                                                answerCount += 1;
                                                finish(res, Answer, answerCount, 1);
                                            });

                                            // CouchDB
                                            var couchDB_Client = new CouchDB_Client();
                                            couchDB_Client.query(function (result) {
                                                Answer.couchDB.time = (Date.now()-time) / 1000 + " s";
                                                Answer.couchDB.data = result;
                                                answerCount += 1;
                                                finish(res, Answer, answerCount, 1);
                                            });

                                        }
                                    });
                                }
                            }
                        });
                    }
                });
            }
        });
    }
};

/**
 * Check
 * @param  {number} count
 * @param  {number} maximum
 * @return {boolean} true or false
 */
var check = function (count, max) {
    max = max*4;
    if(count == max) {
        return true;
    } else {
        return false;
    }
};

/**
 * Finish
 */
var finish = function (res, Answer, answerCount, max) {
    if(check(answerCount, max)) {
        Answer.enviroCar.count = Answer.enviroCar.data.length;
        Answer.parliament.count = Answer.parliament.data.length;
        Answer.postgres.count = Answer.postgres.data.length;
        Answer.couchDB.count = Answer.couchDB.data.length;
        res.status(201).send(Answer);
    }
};
<|endoftext|>


<commit_before>// Loop and control led lighting according to angle and current photo
//
// Lea, Jan 2017

// NOTE: global.bnoValues is an array containing (never null in here):
//         [0]     High-Res Timestamp from process.hrtime() [seconds, nanoseconds]
//         [1]     Heading
//         [2]     Roll
//         [3]     Pitch
//         [4]     gyroX
//         [5]     gyroY
//         [6]     gyroZ
//         [7]     magX
//         [8]     magY
//         [9]     magZ

const regression             = require('regression');

const setCurrentPhotoPromise = require('./image-functions').setCurrentPhotoPromise;
const getCurrentPhoto        = require('./image-functions').getCurrentPhoto;
const getPixelsPromise       = require('./image-functions').getPixelsPromise;
const NUM_LEDS               = require("./image-functions").NUM_LEDS;
const WHITE_ARRAY            = require('./led').WHITE_ARRAY;
const ledLightUp             = require('./led').ledLightUp;

const ANGLE_FIXED_CORRECTION = 90;

// --- Magnet must be attached at the bottom of the reference frame. There we should have angle = 180 deg
//      We use magZ (global.bnoValues[9]) maximum to infer where the absolute bottom is and correct drift
var   angleCorrectionFromBottomMagnet = 0;
var   previousDataPoints = new Array();         // array of [hrtime, currentAngle, magZ]. We keep the last 4 values.

// ----------- TESTS LEA -----------
//var LEA_DEBUG = true;
var LEA_DEBUG = false;
// ---------------------------------

/**
 * Main initialization
 */
function initLoop() {

    // ----------- TESTS LEA -----------
    if (LEA_DEBUG) {
        require("./test/lightup.js");
        return;
    }
    // ---------------------------------

    setCurrentPhotoPromise( process.env.CURRENT_PHOTO_FILENAME )
    .then( fname => {

        // --- Display white strip;
        ledLightUp( WHITE_ARRAY );

        // --- Launch interval func to refresh LED display
        doLedDisplayLoop();
        return;
    })
    .catch( err => {
        console.error("ERROR in initLoop: err =");
        console.error( err );
    });
}

/**
 *  For a given image (currentPhoto) and a given angle, display the LED corresponding colors
 */
function doLedDisplayLoop() {

    let nowHrTime       = process.hrtime();
    let hrTimeDiff      = process.hrtime( global.bnoValues[0] );                // Diff with time of measurement
    let angularVelocity = global.bnoValues[6];
    let angle           = (ANGLE_FIXED_CORRECTION - global.bnoValues[1] + angleCorrectionFromBottomMagnet + 720) % 360;

    // Use angular velocity and elapsed time to improve the currentAngle.
    //  We should always be under 1 sec!
    let currentAngle    = angle;
    let angleDiff       = hrTimeDiff[1] * 1.0e-9 * angularVelocity;
    if ( Math.abs(angleDiff) >= 0.1 ) {
        currentAngle    = (angle + angleDiff + 360 ) % 360;
        console.log( "DIFF angle with velocity:  angle="+ angle +", angleDiff="+ angleDiff
            + ", \t angularVelocity="+ angularVelocity +" deg/s, hrTimeDiff="+ hrTimeDiff );
    }

    // Keep last 4 data points and then correct the angle by detecting the bottom (peak of magZ)
    let newLen = previousDataPoints.unshift( [ nowHrTime, currentAngle, global.bnoValues[9] ] );    // Add as element [0] of the array
    if ( newLen > 3 ) {
        if ( previousDataPoints[1][2] - previousDataPoints[0][2] > 0.1 || previousDataPoints[2][2] - previousDataPoints[0][2] > 1 ) {
            // We have passed magZ maximum! Compute angleCorrectionFromBottomMagnet
            _computeAngleCorrectionFromBottomMagnet();
            console.log( "INFO in do-loop: new value computed for angleCorrectionFromBottomMagnet = "+ angleCorrectionFromBottomMagnet
                         + ", when currentAngle = " + currentAngle );
        }
        else {
            previousDataPoints.slice(0, 3);
        }
    }

    // currentAngle is supposed to correct angle in high rotation speed condition!
    //_doLoop( angle, getCurrentPhoto() );
    _doLoop( currentAngle, getCurrentPhoto() );

    // Loop over within _doLoop when lighting-up is complete
}

/** Internal */
function _doLoop( angle, photoFilename ) {

    getPixelsPromise( angle, photoFilename )
    .then( ledColorArray => {

        if ( ledColorArray && ledColorArray.length == NUM_LEDS ) {

            // Display the colors on the LEDs
            ledLightUp( ledColorArray );
        }

        // Loop
        setTimeout( doLedDisplayLoop, 0 );      // Variant: 1 ms later

    })
    .catch( err => {
        console.error("ERROR in _doLoop: err =");
        console.error( err );
    });
}

/** Internal: diff from previousDataPoints[3][0] */
function _diffHrTime( hrTim) {

    let ns = hrTim[1] - previousDataPoints[3][0][1];
    let s  = hrTim[0] - previousDataPoints[3][0][0];
    return s + ns * 1.0e-9;
}

/** Internal: compute values at bottom through quadratic regression on magZ and currentAngle */
function _computeAngleCorrectionFromBottomMagnet() {

    let data = [ [ 0,                                       previousDataPoints[3][2] ],
                 [ _diffHrTime(previousDataPoints[2][0]),   previousDataPoints[2][2] ],
                 [ _diffHrTime(previousDataPoints[1][0]),   previousDataPoints[1][2] ],
                 [ _diffHrTime(previousDataPoints[0][0]),   previousDataPoints[0][2] ] ];
    let regrMagZ = regression('polynomial', data, 2);
    // magZ = f(t) = regrMagZ[2] * t^2 + regrMagZ[1] * t + regrMagZ[0]

    // --- Now find the Time (in seconds) where magZ is maximum, ie when derivative is 0
    //      2 * regrMagZ[2] * t' + regrMagZ[1] = 0
    let timMagZMax = -0.5 * regrMagZ[1] / regrMagZ[2];
    if ( isNaN(timMagZMax) || timMagZMax < 0 || timMagZMax > data[3][0] ) {        // assert timMagZMax <= data[1][0]
        console.log("\nWARNING in do-loop > _computeAngleCorrectionFromBottomMagnet: IGNORE as timMagZMax="+timMagZMax+" should be 0 <= T <= data[3][0]="+data[3][0]);
        return;
    }

    // --- From there we can interpolate the currentAngle value at that time
    let angData = [ [ 0,            previousDataPoints[3][1] ],
                    [ timMagZMax,   null ],      // null will be filled using the trend, by regression-js
                    [ data[1][0],   previousDataPoints[2][1] ],
                    [ data[2][0],   previousDataPoints[1][1] ],
                    [ data[3][0],   previousDataPoints[0][1] ] ];
    let regrCurrentAngle = regression('polynomial', angData, 2);

    // currentAngleAtMax should be 180, so we auto-correct
    let currentAngleAtMax = angData[1][1];
    console.log("INFO in do-loop > _computeAngleCorrectionFromBottomMagnet: at time="+timMagZMax+", currentAngleAtMax estimated at "+currentAngleAtMax);
    angleCorrectionFromBottomMagnet = (540 - currentAngleAtMax) % 360;    // 180 + 360

    // Empties the data point history as we have found the bottom
    previousDataPoints = new Array();
    return;
}

module.exports.initLoop  = initLoop;
<commit_msg>bf logs<commit_after>// Loop and control led lighting according to angle and current photo
//
// Lea, Jan 2017

// NOTE: global.bnoValues is an array containing (never null in here):
//         [0]     High-Res Timestamp from process.hrtime() [seconds, nanoseconds]
//         [1]     Heading
//         [2]     Roll
//         [3]     Pitch
//         [4]     gyroX
//         [5]     gyroY
//         [6]     gyroZ
//         [7]     magX
//         [8]     magY
//         [9]     magZ

const regression             = require('regression');

const setCurrentPhotoPromise = require('./image-functions').setCurrentPhotoPromise;
const getCurrentPhoto        = require('./image-functions').getCurrentPhoto;
const getPixelsPromise       = require('./image-functions').getPixelsPromise;
const NUM_LEDS               = require("./image-functions").NUM_LEDS;
const WHITE_ARRAY            = require('./led').WHITE_ARRAY;
const ledLightUp             = require('./led').ledLightUp;

const ANGLE_FIXED_CORRECTION = 90;

// --- Magnet must be attached at the bottom of the reference frame. There we should have angle = 180 deg
//      We use magZ (global.bnoValues[9]) maximum to infer where the absolute bottom is and correct drift
var   angleCorrectionFromBottomMagnet = 0;
var   previousDataPoints = new Array();         // array of [hrtime, currentAngle, magZ]. We keep the last 4 values.

// ----------- TESTS LEA -----------
//var LEA_DEBUG = true;
var LEA_DEBUG = false;
// ---------------------------------

/**
 * Main initialization
 */
function initLoop() {

    // ----------- TESTS LEA -----------
    if (LEA_DEBUG) {
        require("./test/lightup.js");
        return;
    }
    // ---------------------------------

    setCurrentPhotoPromise( process.env.CURRENT_PHOTO_FILENAME )
    .then( fname => {

        // --- Display white strip;
        ledLightUp( WHITE_ARRAY );

        // --- Launch interval func to refresh LED display
        doLedDisplayLoop();
        return;
    })
    .catch( err => {
        console.error("ERROR in initLoop: err =");
        console.error( err );
    });
}

/**
 *  For a given image (currentPhoto) and a given angle, display the LED corresponding colors
 */
function doLedDisplayLoop() {

    let nowHrTime       = process.hrtime();
    let hrTimeDiff      = process.hrtime( global.bnoValues[0] );                // Diff with time of measurement
    let angularVelocity = global.bnoValues[6];
    let angle           = (ANGLE_FIXED_CORRECTION - global.bnoValues[1] + angleCorrectionFromBottomMagnet + 720) % 360;

    // Use angular velocity and elapsed time to improve the currentAngle.
    //  We should always be under 1 sec!
    let currentAngle    = angle;
    let angleDiff       = hrTimeDiff[1] * 1.0e-9 * angularVelocity;
    if ( Math.abs(angleDiff) >= 0.1 ) {
        currentAngle    = (angle + angleDiff + 360 ) % 360;
        console.log( "DIFF angle with velocity:  angle="+ angle +", angleDiff="+ angleDiff
            + ", \t angularVelocity="+ angularVelocity +" deg/s, hrTimeDiff="+ hrTimeDiff );
    }

    // Keep last 4 data points and then correct the angle by detecting the bottom (peak of magZ)
    let newLen = previousDataPoints.unshift( [ nowHrTime, currentAngle, global.bnoValues[9] ] );    // Add as element [0] of the array
    if ( newLen > 3 ) {
        if ( previousDataPoints[1][2] - previousDataPoints[0][2] > 0.1 || previousDataPoints[2][2] - previousDataPoints[0][2] > 1 ) {
            // We have passed magZ maximum! Compute angleCorrectionFromBottomMagnet
            _computeAngleCorrectionFromBottomMagnet();
        }
        else {
            previousDataPoints.slice(0, 3);
        }
    }

    // currentAngle is supposed to correct angle in high rotation speed condition!
    //_doLoop( angle, getCurrentPhoto() );
    _doLoop( currentAngle, getCurrentPhoto() );

    // Loop over within _doLoop when lighting-up is complete
}

/** Internal */
function _doLoop( angle, photoFilename ) {

    getPixelsPromise( angle, photoFilename )
    .then( ledColorArray => {

        if ( ledColorArray && ledColorArray.length == NUM_LEDS ) {

            // Display the colors on the LEDs
            ledLightUp( ledColorArray );
        }

        // Loop
        setTimeout( doLedDisplayLoop, 0 );      // Variant: 1 ms later

    })
    .catch( err => {
        console.error("ERROR in _doLoop: err =");
        console.error( err );
    });
}

/** Internal: diff from previousDataPoints[3][0] */
function _diffHrTime( hrTim) {

    let ns = hrTim[1] - previousDataPoints[3][0][1];
    let s  = hrTim[0] - previousDataPoints[3][0][0];
    return s + ns * 1.0e-9;
}

/** Internal: compute values at bottom through quadratic regression on magZ and currentAngle */
function _computeAngleCorrectionFromBottomMagnet() {

    let data = [ [ 0,                                       previousDataPoints[3][2] ],
                 [ _diffHrTime(previousDataPoints[2][0]),   previousDataPoints[2][2] ],
                 [ _diffHrTime(previousDataPoints[1][0]),   previousDataPoints[1][2] ],
                 [ _diffHrTime(previousDataPoints[0][0]),   previousDataPoints[0][2] ] ];
    let regrMagZ = regression('polynomial', data, 2);
    // magZ = f(t) = regrMagZ[2] * t^2 + regrMagZ[1] * t + regrMagZ[0]

    // --- Now find the Time (in seconds) where magZ is maximum, ie when derivative is 0
    //      2 * regrMagZ[2] * t' + regrMagZ[1] = 0
    let timMagZMax = -0.5 * regrMagZ[1] / regrMagZ[2];
    if ( isNaN(timMagZMax) || timMagZMax < 0 || timMagZMax > data[3][0] ) {        // assert timMagZMax <= data[1][0]
        console.log("\nWARNING in do-loop > _computeAngleCorrectionFromBottomMagnet: IGNORE as timMagZMax="+timMagZMax+" should be 0 <= T <= data[3][0]="+data[3][0]);
        return;
    }

    // --- From there we can interpolate the currentAngle value at that time
    let angData = [ [ 0,            previousDataPoints[3][1] ],
                    [ timMagZMax,   null ],      // null will be filled using the trend, by regression-js
                    [ data[1][0],   previousDataPoints[2][1] ],
                    [ data[2][0],   previousDataPoints[1][1] ],
                    [ data[3][0],   previousDataPoints[0][1] ] ];
    let regrCurrentAngle = regression('polynomial', angData, 2);

    // currentAngleAtMax should be 180, so we auto-correct
    let currentAngleAtMax = angData[1][1];
    angleCorrectionFromBottomMagnet = (540 - currentAngleAtMax) % 360;    // 180 + 360
    console.log("\nINFO in do-loop > _computeAngleCorrectionFromBottomMagnet: at time="+timMagZMax
                + ", currentAngleAtMax estimated at "+currentAngleAtMax
                + ", NEW VALUE angleCorrectionFromBottomMagnet="+angleCorrectionFromBottomMagnet );

    // Empties the data point history as we have found the bottom
    previousDataPoints = new Array();
    return;
}

module.exports.initLoop  = initLoop;
<|endoftext|>