verse/verse-javascript

View on GitHub
src/tag.js

Summary

Maintainability
F
1 wk
Test Coverage
/*
 * Verse Websocket Asynchronous Module 
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2013-2014 Jiri Vrany, Jiri Hnidek
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

/* globals define */

define(['Int64', 'command'], function(Int64, command) {
    'use strict';

    var commands, get_routines, set_routines, data_types, data_type_len, op_codes, tag,
        getTagSetCommons, getTagSetUint8, getTagSetUint16,
        getTagSetUint32, getTagSetUint64, getTagSetFloat16,
        getTagSetFloat32, getTagSetFloat64, getTagSetString8;

    /*
    * common function for all tagSet commands 
    */
    getTagSetCommons = function getTagSetCommons(opCode, receivedView, bufferPosition) {
        return {
            CMD: commands[opCode],
            SHARE: receivedView.getUint8(bufferPosition + 2),
            NODE_ID: receivedView.getUint32(bufferPosition + 3),
            TAG_GROUP_ID: receivedView.getUint16(bufferPosition + 7),
            TAG_ID: receivedView.getUint16(bufferPosition + 9),
            VALUES: []
        };
    };

    /*
    * common function for all SetUint8 opCodes
    * @param opCode int from interval 70 - 73
    */
    getTagSetUint8 = function getTagSetUint8(opCode, receivedView, bufferPosition) {
        
        var result = getTagSetCommons(opCode, receivedView, bufferPosition);

        result.VALUES[0] = receivedView.getUint8(bufferPosition + 11);

        /* istanbul ignore else  */
        if (opCode > 70) {
            result.VALUES[1] = receivedView.getUint8(bufferPosition + 12);   
        }

        /* istanbul ignore else  */
        if (opCode > 71) {
            result.VALUES[2] = receivedView.getUint8(bufferPosition + 13);   
        }

        /* istanbul ignore else  */
        if (opCode > 72) {
            result.VALUES[3] = receivedView.getUint8(bufferPosition + 14);   
        }

        return result;
    };

    /*
    * common function for all SetUint16 opCodes
    * @param opCode int from interval 74 - 77
    */
    getTagSetUint16 = function getTagSetUint16(opCode, receivedView, bufferPosition) {
        var result = getTagSetCommons(opCode, receivedView, bufferPosition);

        result.VALUES[0] = receivedView.getUint16(bufferPosition + 11);

        /* istanbul ignore else  */
        if (opCode > 74) {
            result.VALUES[1] = receivedView.getUint16(bufferPosition + 13);   
        }

        /* istanbul ignore else  */
        if (opCode > 75) {
            result.VALUES[2] = receivedView.getUint16(bufferPosition + 15);   
        }

        /* istanbul ignore else  */
        if (opCode > 76) {
            result.VALUES[3] = receivedView.getUint16(bufferPosition + 17);   
        }

        return result;
    };

    /*
    * common function for all SetUint32 opCodes
    * @param opCode int from interval 78 - 81
    */

    getTagSetUint32 = function getTagSetUint32(opCode, receivedView, bufferPosition) {
        var result = getTagSetCommons(opCode, receivedView, bufferPosition);

        result.VALUES[0] = receivedView.getUint32(bufferPosition + 11);

        /* istanbul ignore else  */
        if (opCode > 78) {
            result.VALUES[1] = receivedView.getUint32(bufferPosition + 15);   
        }

        /* istanbul ignore else  */
        if (opCode > 79) {
            result.VALUES[2] = receivedView.getUint32(bufferPosition + 19);   
        }

        /* istanbul ignore else  */
        if (opCode > 80) {
            result.VALUES[3] = receivedView.getUint32(bufferPosition + 23);   
        }

        return result;
    };

    /*
    * common function for all SetUint64 opCodes
    * WARNING > conversion by valueOf fails if the number is bigger than 2^53
    * @param opCode int from interval 82 - 85
    *
    */
    getTagSetUint64 = function getTagSetUint64(opCode, receivedView, bufferPosition) {
        var result, hi, lo, bigNumber;

        result = getTagSetCommons(opCode, receivedView, bufferPosition);

        lo = receivedView.getUint32(bufferPosition + 11);
        hi = receivedView.getUint32(bufferPosition + 15); 
        bigNumber = new Int64(hi, lo);
        result.VALUES[0] = bigNumber.valueOf();

        /* istanbul ignore else  */
        if (opCode > 82) {
            lo = receivedView.getUint32(bufferPosition + 19);
            hi = receivedView.getUint32(bufferPosition + 23); 
            bigNumber = new Int64(hi, lo);
            result.VALUES[1] = bigNumber.valueOf();
        }

        /* istanbul ignore else  */
        if (opCode > 83) {
            lo = receivedView.getUint32(bufferPosition + 27);
            hi = receivedView.getUint32(bufferPosition + 31); 
            bigNumber = new Int64(hi, lo);
            result.VALUES[2] = bigNumber.valueOf();
        }

        /* istanbul ignore else  */    
        if (opCode > 84) {
            lo = receivedView.getUint32(bufferPosition + 35);
            hi = receivedView.getUint32(bufferPosition + 39); 
            bigNumber = new Int64(hi, lo);
            result.VALUES[3] = bigNumber.valueOf();
        }

        return result;
    };

    /*
    * common function for all SetReal32 opCodes
    * @param opCode int from interval 90 - 93
    */
    /* istanbul ignore next */
    getTagSetFloat16 = function getTagSetFloat16(opCode, receivedView, bufferPosition) {
        var result = getTagSetCommons(opCode, receivedView, bufferPosition);

        result.VALUES[0] = 'Float16 not supported in JS';

        return result;
    };

    /*
    * common function for all SetReal32 opCodes
    * @param opCode int from interval 90 - 93
    */
    getTagSetFloat32 = function getTagSetFloat32(opCode, receivedView, bufferPosition) {
        var result = getTagSetCommons(opCode, receivedView, bufferPosition);

        result.VALUES[0] = receivedView.getFloat32(bufferPosition + 11);

        /* istanbul ignore else  */
        if (opCode > 90) {
            result.VALUES[1] = receivedView.getFloat32(bufferPosition + 15);   
        }

        /* istanbul ignore else  */
        if (opCode > 91) {
            result.VALUES[2] = receivedView.getFloat32(bufferPosition + 19);   
        }

        /* istanbul ignore else  */
        if (opCode > 92) {
            result.VALUES[3] = receivedView.getFloat32(bufferPosition + 23);   
        }

        return result;
    };

    /*
    * common function for all SetReal64 opCodes
    * @param opCode int from interval 94 - 97
    */
    getTagSetFloat64 = function getTagSetFloat64(opCode, receivedView, bufferPosition) {
        var result = getTagSetCommons(opCode, receivedView, bufferPosition);

        result.VALUES[0] = receivedView.getFloat64(bufferPosition + 11);

        /* istanbul ignore else  */
        if (opCode > 94) {
            result.VALUES[1] = receivedView.getFloat64(bufferPosition + 19);   
        }

        /* istanbul ignore else  */
        if (opCode > 95) {
            result.VALUES[2] = receivedView.getFloat64(bufferPosition + 27);   
        }

        /* istanbul ignore else  */
        if (opCode > 96) {
            result.VALUES[3] = receivedView.getFloat64(bufferPosition + 35);   
        }

        return result;
    };

    /*
    * common function for all SetReal64 opCodes
    * @param opCode int from interval 94 - 97
    */
    getTagSetString8 = function getTagSetString8(opCode, receivedView, bufferPosition) {
        var i, strLength, result;

        result = getTagSetCommons(opCode, receivedView, bufferPosition);
        delete result.VALUES;
        result.VALUE = '';

        strLength = receivedView.getUint8(bufferPosition + 11);
        
        for (i = 0; i < strLength; i++) {
            result.VALUE += String.fromCharCode(receivedView.getUint8(bufferPosition + 12 + i));
        
        }
        
        return result;
    };


    // Command codes = opCodes
    commands = {
        68: 'TAG_CREATE',
        69: 'TAG_DESTROY',
        70: 'TAG_SET_UINT8',
        71: 'TAG_SET_UINT8',
        72: 'TAG_SET_UINT8',
        73: 'TAG_SET_UINT8',
        74: 'TAG_SET_UINT16',
        75: 'TAG_SET_UINT16',
        76: 'TAG_SET_UINT16',
        77: 'TAG_SET_UINT16',
        78: 'TAG_SET_UINT32',
        79: 'TAG_SET_UINT32',
        80: 'TAG_SET_UINT32',
        81: 'TAG_SET_UINT32',
        82: 'TAG_SET_UINT64',
        83: 'TAG_SET_UINT64',
        84: 'TAG_SET_UINT64',
        85: 'TAG_SET_UINT64',
        86: 'TAG_SET_REAL16',
        87: 'TAG_SET_REAL16',
        88: 'TAG_SET_REAL16',
        89: 'TAG_SET_REAL16',
        90: 'TAG_SET_REAL32',
        91: 'TAG_SET_REAL32',
        92: 'TAG_SET_REAL32',
        93: 'TAG_SET_REAL32',
        94: 'TAG_SET_REAL64',
        95: 'TAG_SET_REAL64',
        96: 'TAG_SET_REAL64',
        97: 'TAG_SET_REAL64',
        98: 'TAG_SET_STRING8'
    };


    /*
     * get_routines - parsing functions for tag commands from server
     */
    get_routines = {
        68: function getTagCreate(opCode, receivedView, bufferPosition) {
            var result;
            result = getTagSetCommons(opCode, receivedView, bufferPosition);
            delete result.VALUES;  
            result.DATA_TYPE = receivedView.getUint8(bufferPosition + 11);
            result.COUNT = receivedView.getUint8(bufferPosition + 12);
            result.CUSTOM_TYPE = receivedView.getUint16(bufferPosition + 13);
            return result;
            
        },
        69: function getTagDestroy(opCode, receivedView, bufferPosition) {
            var result;
            result = getTagSetCommons(opCode, receivedView, bufferPosition);
            delete result.VALUES;    

            return result;
        },
        70: getTagSetUint8,
        71: getTagSetUint8,
        72: getTagSetUint8,
        73: getTagSetUint8,
        74: getTagSetUint16,
        75: getTagSetUint16,
        76: getTagSetUint16,
        77: getTagSetUint16,
        78: getTagSetUint32,
        79: getTagSetUint32,
        80: getTagSetUint32,
        81: getTagSetUint32,
        82: getTagSetUint64,
        83: getTagSetUint64,
        84: getTagSetUint64,
        85: getTagSetUint64,
        86: getTagSetFloat16,
        87: getTagSetFloat16,
        88: getTagSetFloat16,
        89: getTagSetFloat16,
        90: getTagSetFloat32,
        91: getTagSetFloat32,
        92: getTagSetFloat32,
        93: getTagSetFloat32,
        94: getTagSetFloat64,
        95: getTagSetFloat64,
        96: getTagSetFloat64,
        97: getTagSetFloat64,
        98: getTagSetString8
    };

    /*
     * set_routines - setting values of tags
     */
    set_routines = {
        'UINT8': function setTagSetUint8(view, values) {
            for (var i = 0; i < values.length; i++) {
                view.setUint8(11 + i, values[i]);
            }
        },
        'UINT16': function setTagSetUint16(view, values) {
            for (var i = 0; i < values.length; i++) {
                view.setUint16(11 + 2 * i, values[i]);
            }
        },
        'UINT32': function setTagSetUint32(view, values) {
            for (var i = 0; i < values.length; i++) {
                view.setUint32(11 + 4 * i, values[i]);
            }
        },
        'UINT64': function setTagSetUint64(view, values) {
            for (var i = 0; i < values.length; i++) {
                // Javascript currently supports only 32-bit integers
                view.setUint32(11 + 4 + 8 * i, values[i]);
            }
        },
        'REAL16': null,
        'REAL32': function setTagSetReal32(view, values) {
            for (var i = 0; i < values.length; i++) {
                view.setFloat32(11 + 4 * i, values[i]);
            }
        },
        'REAL64': function setTagSetReal64(view, values) {
            for (var i = 0; i < values.length; i++) {
                view.setFloat64(11 + 8 * i, values[i]);
            }
        },
        'STRING8': function setTagSetString8(view, values) {
            var n;
            // Encode length of the string
            view.setUint8(11, values[0].length);
            for (var i = 0; i < values[0].length; i++) {
                n = values[0].charCodeAt(i);
                if (n >= 0 && n < 128) {
                    view.setUint8(12 + i, n);
                }
                // TODO: encode properly other unicode values
            }
        }
    };

    /*
     * allowed names of tag data types
     */
    data_types = {
        'UINT8': 1,
        'UINT16': 2,
        'UINT32': 3,
        'UINT64': 4,
        'REAL16': 5,
        'REAL32': 6,
        'REAL64': 7,
        'STRING8': 8
    };

    /*
     * data types length in memory
     */
    data_type_len = {
        'UINT8': 1,
        'UINT16': 2,
        'UINT32': 4,
        'UINT64': 8,
        'REAL16': 2,
        'REAL32': 4,
        'REAL64': 8
    };

    /*
     * basic OpCodes (with one value) for data types
     */
    op_codes = {
        'UINT8': 70,
        'UINT16': 74,
        'UINT32': 78,
        'UINT64': 82,
        'REAL16': 86,
        'REAL32': 90,
        'REAL64': 94
    };

    tag = {

        /*
         * parse received buffer for tag command VALUES
         */
        getTagValues: function getTagValues(opCode, receivedView, bufferPosition, length) {
            var result = get_routines[opCode](opCode, receivedView, bufferPosition, length);
            return result;
        },

        /*
         * create new tag at verse server
         */
        create: function(nodeId, tagGroupId, dataType, count, customType) {
            var cmd, view;
            cmd = command.template(15, 68);
            view = new DataView(cmd);
            view.setUint8(2, 0); //share
            view.setUint32(3, nodeId);
            view.setUint16(7, tagGroupId);
            view.setUint16(9, 65535); // tag ID will be defined by server
            if ( data_types.hasOwnProperty(dataType) ) {
                view.setUint8(11, data_types[dataType]);
            } else {
                return null;
            }
            view.setUint8(12, count);
            view.setUint16(13, customType);
            return cmd;
        },

        /*
         * create new tag at verse server
         */
        destroy: function(nodeId, tagGroupId, tagId) {
            var cmd, view;
            cmd = command.template(11, 69);
            view = new DataView(cmd);
            view.setUint8(2, 0); //share
            view.setUint32(3, nodeId);
            view.setUint16(7, tagGroupId);
            view.setUint16(9, tagId);
            return cmd;
        },

        /*
         * set/change value of tag at verse server
         */
         set: function(nodeId, tagGroupId, tagId, dataType, values) {
            var cmd, view, cmd_len;
            // Compute length of the command and create almost empty command
            if ( data_types.hasOwnProperty(dataType) ) {
                if (dataType === 'STRING8') {
                    // Tag can contain only one string!
                    cmd_len = 11 + 1 + values[0].length;
                    cmd = command.template(cmd_len, 98);
                } else {
                    cmd_len = 11 + ( values.length * data_type_len[dataType] );
                    cmd = command.template(cmd_len, op_codes[dataType] + values.length - 1);
                }
            } else {
                return null;
            }
            view = new DataView(cmd);
            view.setUint8(2, 0); //share
            view.setUint32(3, nodeId);
            view.setUint16(7, tagGroupId);
            view.setUint16(9, tagId);
            set_routines[dataType](view, values);

            return cmd;
         }
    };

    return tag;

});