Metal Detector / js /

app.model.js

/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*global window, tizen, console*/

/**
 * Application model module.
 * It is responsible for initializing and managing magnetic sensor.
 * It also checks if magnetic sensor is supported.
 *
 * @module app.model
 * @requires {@link app.common}
 * @namespace app.model
 * @memberof app
 */

// make sure that "app" namespace is created
window.app = window.app || {};

(function defineAppModel(app) {
    'use strict';

    /**
     * Sensor type constant string.
     *
     * @memberof app.model
     * @private
     * @const {string}
     */
    var SENSOR_TYPE = 'MAGNETIC',

        /**
         * Magnetometer capability key.
         *
         * @memberof app.model
         * @private
         * @const {string}
         */
        MAGNETOMETER_CAPABILITY =
            'http://tizen.org/feature/sensor.magnetometer',

        /**
         * Maximal signal strength.
         *
         * @memberof app.model
         * @private
         * @const {number}
         */
        MAX_SIGNAL_STRENGTH = 5000,

        /**
         * Error types constants.
         *
         * @memberof app.model
         * @public
         * @const {object<string, string>}
         * @namespace app.model.ERROR
         */
        ERROR = Object.freeze({

            /**
             * Error raised when magnetic sensor is not supported by
             * the device.
             *
             * @memberof app.model.ERROR
             * @const {string}
             */
            NOT_SUPPORTED: 'not_supported',

            /**
             * Error raised when sensor start procedure fails.
             *
             * @memberof app.model.ERROR
             * @const {string}
             */
            START_FAILURE: 'start_failure',

            /**
             * Raised when unknown error occurs.
             *
             * @memberof app.model.ERROR
             * @const {string}
             */
            UNKNOWN: 'unknown'
        }),

        /**
         * Magnetic sensor object.
         *
         * @memberof app.model
         * @private
         * @type {Sensor}
         */
        sensor = null,

        /**
         * Flag sensor started.
         *
         * @memberof app.model
         * @private
         * @type {boolean}
         */
        isSensorStarted = false,

        /**
         * Sensor service.
         *
         * @memberof app.model
         * @private
         * @type {SensorService}
         */
        sensorService = null,

        /**
         * Dispatches an event.
         *
         * @memberof app.model
         * @private
         * @type {function}
         */
        dispatch = app.common.dispatchEvent;

    // create namespace for the module
    app.model = app.model || {};

    /**
     * Converts the number value expressed in radians to degrees.
     *
     * @memberof app.model
     * @private
     * @param {number} value
     * @returns {number}
     */
    function toDegrees(value) {
        return value * 180 / Math.PI;
    }

    /**
     * Returns rotation value in degrees for view elements based on
     * electromagnetic strength of x and y axis vectors.
     *
     * Technically: calculates resultant vector deflection degree created by
     * adding x and y vectors.
     *
     * @memberof app.model
     * @private
     * @param {number} x
     * @param {number} y
     * @returns {number}
     */
    function calculateRotation(x, y) {
        return -toDegrees(Math.atan2(x, -y));
    }

    /**
     * Returns resultant vector signal strength created from signal strength
     * vectors for each axis.
     *
     * @memberof app.model
     * @private
     * @param {number} x
     * @param {number} y
     * @param {number} z
     * @returns {number}
     */
    function calculateSignalStrength(x, y, z) {
        return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
    }

    /**
     * Returns signal strength relative to maximal signal strength in
     * interval [0, maxNumber]. Uses logarithmic scale.
     *
     * @memberof app.model
     * @public
     * @param {number} signalStrength
     * @param {number} maxNumber
     * @returns {number}
     */
    function calculateRelativeSignalStrength(signalStrength, maxNumber) {
        return Math.ceil(signalStrength > MAX_SIGNAL_STRENGTH ? maxNumber :
                Math.log(signalStrength) / Math.log(MAX_SIGNAL_STRENGTH) *
                maxNumber);
    }

    /**
     * Get sensor data success callback.
     *
     * @memberof app.model
     * @private
     * @fires "model.magnetic.update"
     * @param {SensorMagneticData} sensorData
     */
    function onGetSuccess(sensorData) {
        var x = sensorData.x,
            y = sensorData.y,
            z = sensorData.z,
            data = {
                value: calculateSignalStrength(x, y, z),
                rotation: calculateRotation(x, y),
                rotationVisible: (x <= -1 || x >= 1) || (y <= -1 || y >= 1) ?
                    true : false
            };

        dispatch('model.magnetic.update', data);
    }

    /**
     * Start sensor success callback.
     * Sets isSensorStarted flag.
     *
     * @memberof app.model
     * @private
     * @fires "model.magnetic.ready"
     */
    function onSensorStartSuccess() {
        isSensorStarted = true;
        dispatch('model.magnetic.ready');
        try {
            sensor.setChangeListener(onGetSuccess);
        } catch (e) {
            console.error('Execption occured during setting thee listener', e);
        }
    }

    /**
     * Start sensor error callback.
     *
     * @memberof app.model
     * @private
     * @fires "model.magnetic.error"
     */
    function onSensorStartError() {
        isSensorStarted = false;
        dispatch('model.magnetic.error', ERROR.START_FAILURE);
    }

    /**
     * Starts sensor.
     * Sets change listener for data.
     * If isSensorStarted is true returns null.
     *
     * @memberof app.model
     * @public
     */
    function startSensor() {
        if (isSensorStarted) {
            return;
        }

        try {
            sensor.start(onSensorStartSuccess, onSensorStartError);
        } catch (e) {
            console.error('Exception occured during starting the sensor', e);
            onSensorStartError();
        }
    }

    /**
     * Stops refresh sensor data.
     * Unsets change listener for data.
     * Sets isSensorStarted to false.
     * If isSensorStarted is false returns null.
     *
     * @memberof app.model
     * @public
     */
    function stopSensor() {
        isSensorStarted = false;
        try {
            sensor.unsetChangeListener();
            sensor.stop();
        } catch (e) {
            console.error('Exception occurred during stopping the sensor', e);
        }
    }

    /**
     * Exits application. Stops sensor.
     *
     * @memberof app.model
     * @public
     */
    function exitApplication() {
        if (sensor) {
            stopSensor();
        }

        try {
            tizen.application.getCurrentApplication().exit();
        } catch (e) {
            console.error('Error:', e.message);
        }
    }

    /**
     * Initializes module.
     *
     * @memberof app.model
     * @public
     */
    function init() {
        var errorType = '';

        // check sensor capability
        try {
            if (!tizen.systeminfo.getCapability(MAGNETOMETER_CAPABILITY)) {
                dispatch('model.magnetic.error', ERROR.NOT_SUPPORTED);
            }
        } catch (e) {
            dispatch('model.magnetic.error', ERROR.UNKNOWN);
        }

        // check if sensor is available
        sensorService = tizen.sensorservice || null;

        try {
            sensor = sensorService.getDefaultSensor(SENSOR_TYPE);
        } catch (error) {
            if (error.type === 'NotSupportedError') {
                errorType = ERROR.NOT_SUPPORTED;
            } else {
                errorType = ERROR.UNKNOWN;
            }

            dispatch('model.magnetic.error', errorType);
            return;
        }
        startSensor();
    }

    app.model.ERROR = ERROR;
    app.model.startSensor = startSensor;
    app.model.stopSensor = stopSensor;
    app.model.calculateRelativeSignalStrength = calculateRelativeSignalStrength;
    app.model.exitApplication = exitApplication;
    app.model.init = init;

})(window.app);