简体   繁体   中英

Why I can't get the global value in a cordova app?

I'm setting outputTagJson as a global variable for the cordova app object. However, I can't display the entire JSON when I tried to display that in function readDataBlock: function() , only one message will be received and will be duplicated. If I display the JSON inside onListeningData: function(data) , the data is correct and complete. I'm thinking the part that can cause this problem is from the concat part where I concatenate each response with a carriage return (ASCII code = 13). However, I didn't see a chance to encounter the problem. In fact, I think my problem can be described more generic as: I set the global variable in javascript, but I can't get the complete value out of some function scope.

var app = {
    macAddress: "00:0B:CE:07:36:AB",  // get your mac address from bluetoothSerial.list
    chars: "",
    responseGroup: [],
    responseHeader: [''],
    outputTagJson: {},
    timeList: [],
    Execution_List: ['M,0\rU,18\rGR\rG,LOCATE,04,0,1,3\rO,2,30,0,0\rR,8\rS,2\rM,433\r'],

/*
    Application constructor
 */
    initialize: function() {
        this.bindEvents();
        console.log("Starting SimpleSerial app");
    },
/*
    bind any events that are required on startup to listeners:
*/
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
        connectButton.addEventListener('touchend', app.Connect, false);
        scanButton.addEventListener('touchend', app.disCover, false);
    },

/*
    this runs when the device is ready for user interaction:
*/
    onDeviceReady: function() {
        // check to see if Bluetooth is turned on.
        // this function is called only
        //if isEnabled(), below, returns success:

        var listPorts = function() {
            // list the available BT ports:
            bluetoothSerial.list(
                function(results) {
                    app.display(JSON.stringify(results));
                },
                function(error) {
                    app.display(JSON.stringify(error));
                }
            );
        };

        // if isEnabled returns failure, this function is called:
        var notEnabled = function() {
            app.display("Bluetooth is not enabled.")
        };

         // check if Bluetooth is on:
        bluetoothSerial.isEnabled(
            listPorts,
            notEnabled
        );
    },

    disCover: function() {
        var scan = function () {
            app.clear();
            app.display("Scanning available Devices...");

            bluetoothSerial.discoverUnpaired(
                function(results) {
                    app.display(JSON.stringify(results));
                },
                function(error) {
                    app.display(JSON.stringify(error));
                }
            );
        };

        var notEnabled = function() {
            app.display("Bluetooth is not enabled.")
        };

        bluetoothSerial.isEnabled(
            scan,
            notEnabled
        );

    },

/*
    Connects if not connected, and disconnects if connected:
*/
    Connect: function() {

        // connect() will get called only if isConnected() (below)
        // returns failure. In other words, if not connected, then connect:
        var connect = function () {
            // if not connected, do this:
            // clear the screen and display an attempt to connect
            app.clear();
            app.display("Attempting to connect. " +
                "Make sure the serial port is open on the target device.");
            app.display("Connected to: " + app.macAddress);
            connectButton.innerHTML = "Disconnect";
            // attempt to connect:
            bluetoothSerial.connect(
                app.macAddress,  // device to connect to
                app.connectSuccess,    // start listening if you succeed
                app.showError    // show the error if you fail
            );
        };

        // disconnect() will get called only if isConnected() (below)
        // returns success  In other words, if  connected, then disconnect:
        var disconnect = function () {
            app.display("attempting to disconnect");
            // if connected, do this:
            bluetoothSerial.disconnect(
                app.closePort,     // stop listening to the port
                app.showError      // show the error if you fail
            );
        };

        // here's the real action of the manageConnection function:
        bluetoothSerial.isConnected(disconnect, connect);
    },
/*
    subscribes to a Bluetooth serial listener for newline
    and changes the button:
*/

    getBatteryInfo: function() {
        var Bcomm = 'WB\r';
        app.writeCommand(Bcomm);
    },

    // On Connection Success, we want to display battery info
    // and send command immediately
    connectSuccess: function() {
        app.getBatteryInfo();
        for (var i = 0; i < app.Execution_List.length;i++)
        {
            (function(n) {
                app.writeCommand(app.Execution_List[n]);
            })(i);
        }
    },

    // Wrapper of bluetooth write, read reponse immediately 
    // after sending commands
    writeCommand: function(data) {
        bluetoothSerial.write(data, 
            function(data) {
                console.log('sending %s',data);
        }, function(error){
            console.log(error);
        });
        app.readDataBlock();
        app.responseGroup = [];
    },

/*
    Processor part:
    Parsing Response String and out a JSON of each Attributes
*/
    onParsing: function(data){
        var alphabeticRegex = /^[A-Z]+$/i;          // Response header start with an alphabetic string
        var decimalRegex = /^\d+\.?\d*$/;
        app.outputTagJson = {};
        if (data == null || data == '\r')           // Sanity Check
        {
            return;
        }
        Element = data.split(',');                  
        while (true)                                // For some response, it didn't start with 'A-z'
        {
            if (alphabeticRegex.test(Element[0]) != true)
            {
                Element.splice(0,1);
            }
            else
            {
                break;
            }
        }
        // Parsing each options, not generic, have to deal with each specific case.
        switch (Element[0]) {
            case 'VV': 
                app.outputTagJson.readerIdentifier = {};
                app.outputTagJson.readerIdentifier.buildID = Element[2];
                app.outputTagJson.readerIdentifier.modelID = Element[3];
                break;
            case 'V':
                app.outputTagJson.Battery = {};
                app.outputTagJson.Battery.percentage = Element[2];
                if (Element[3] != null)
                {
                    app.outputTagJson.Battery.chargingmode = "Charging";
                }
                else
                {
                    app.outputTagJson.Battery.chargingmode = "Not charged";
                }
                break;
            case 'U':
                app.outputTagJson.timeStamp = {};
                app.outputTagJson.timeStamp.range = 10;
                app.outputTagJson.timeStamp.range = 1.00;
                break;
            case 'M':
                app.outputTagJson.readerMode = {};
                if (Element[1] == 0) app.outputTagJson.readerMode.message = 'Standby';
                if (Element[1] == 20) app.outputTagJson.readerMode.message = '303MHz';
                if (Element[1] == 40) app.outputTagJson.readerMode.message = 'Japanese 303MHz';
                if (Element[1] == 433) app.outputTagJson.readerMode.message = '433MHz';
                else app.outputTagJson.readerMode.message = 'In motion';
                break;
            case 'H':
                var len = Element.length;
                var props = ['tagID','groupID','payload','timeStamp','sigStrenthA','sigStrenthB'];
                app.outputTagJson.Tag = {};
                if (len >= 2)
                {
                    app.outputTagJson.Tag[props[0]] = Element[1];
                    app.outputTagJson.Tag.isLost = "False";
                    for(var i=3;i<=len;i++)
                        app.outputTagJson.Tag[props[i-2]] = Element[i-1].slice(1,Element[i-1].length);
                }
                break;
            case 'L':
                var len = Element.length;
                var props = ['tagID','groupID','payload','timeStamp'];
                app.outputTagJson.Tag = {};
                if (len >= 2)
                {
                    app.outputTagJson.Tag[props[0]] = Element[1];
                    app.outputTagJson.Tag.isLost = "True";
                    for(var i=3;i<=len;i++)
                        app.outputTagJson.Tag[props[i-2]] = Element[i-1].slice(1,Element[i-1].length);
                }
                break;
            case 'E':
                app.outputTagJson.Error = {};
                if (Element[1] == 2) app.outputTagJson.Error.message = "Received command is invalid";
                if (Element[1] == 3) app.outputTagJson.Error.message = "Command has too many parameters";
                if (Element[1] == 4) app.outputTagJson.Error.message = "A command parameter is out of range";
                if (Element[1] == 5) app.outputTagJson.Error.message = "Maximum number of group codes is exceeded";
                if (Element[1] == 6) app.outputTagJson.Error.message = "Character buffer is full. One or more tag reports were dropped";
                if (Element[1] == 7) app.outputTagJson.Error.message = "DSP offline. Indicates DSP failure or failed upgrade of DSP firmware";
                if (Element[1] == 8) app.outputTagJson.Error.message = "Access denied. Indicates security is enabled and a valid ':login' is required";
                break;
            default:
                break;
        }
    },

    // Listening to data, function will be continuous invoked
    // until there's no more incoming data or unsubscribe/disconnect
    // appears
    onListeningData: function(data) {
        var responseLine = [];
        console.log('Receiving data from reader');
        // Convert raw data (binary arrays) to Unit8Array (ASCII code)
        var bytes = new Uint8Array(data);
        // Concat all integer until carriage return appears.
        for (var i = 0; i< bytes.length; i++)
        {
            responseLine.push(bytes[i]);
            if (bytes[i] == 13)
            {
                app.responseGroup.push(responseLine);
                app.responseGroup = Array.prototype.concat.apply([],app.responseGroup);
                responseLine = [];
            }
            else if (bytes[i] != 13 && i == bytes.length-1)
            {
                app.responseGroup.push(responseLine);
            }
        }

        // Once we get the carriage return, parse it to string
        // Do sanity check for null data and single carriage return
        // single carraige return appears when <set-like> command is sent
        // to the reader (a mark as <set-like> command success)
        try {
            var i = 0;
            if (app.responseGroup.length > 0)
            {
                while (i<app.responseGroup.length)
                {
                    if (app.responseGroup[i] == 13)
                    {
                        var strGroup = app.responseGroup.splice(0,i+1);
                        strGroup.splice(-1);
                        var response = String.fromCharCode.apply(null, strGroup);
                        app.onParsing(response);
                        //app.display(JSON.stringify(app.outputTagJson));
                        i = 0;
                    }
                    else
                    {
                        i++;
                    }
                }
            }
            else{
                throw err;
            }
        } catch(err) {
            console.log(err);
        }
    },

    readDataBlock: function() {
        // set up a listener to listen for newlines
        // and display any new data that's come in since
        // the last newline:
        console.log('going to send data to the reader');
        //'M,0\rGR\rG,LOCATE,04,0,1,3\rO,2,30,0,0\rR,8\rS,2\rM,433\r'
        //'G,LOCATE,04,0,0,0\rS,2\rO,1,0,0,2\rU,18\rN,0\rM,433\rZ,99,99\r'
        bluetoothSerial.subscribeRawData(app.onListeningData,
            function(error){
                console.log(error);
        });
        if (JSON.stringify(app.outputTagJson) != "{}")
            app.display(JSON.stringify(app.outputTagJson));
    },

/*
    unsubscribes from any Bluetooth serial listener and changes the button:
*/
    closePort: function() {
        // if you get a good Bluetooth serial connection:
        app.display("Disconnected from: " + app.macAddress);
        // change the button's name:
        connectButton.innerHTML = "Connect";
        // unsubscribe from listening:
        bluetoothSerial.unsubscribe(
                function (data) {
                    app.display(data);
                },
                app.showError
        );
    },
/*
    appends @error to the message div:
*/
    showError: function(error) {
        app.display(error);
    },

/*
    appends @message to the message div:
*/
    display: function(message) {
        var display = document.getElementById("message"), // the message div
            lineBreak = document.createElement("p"),     // a line break
            label = document.createTextNode(message);     // create the label

        display.appendChild(lineBreak);          // add a line break
        display.appendChild(label);              // add the message node
    },
/*
    clears the message div:
*/
    clear: function() {
        var display = document.getElementById("message");
        display.innerHTML = "";
    }
};      // end of app

Your problem is scope of your function. When you try to use app.something inside of app you will create new var app inside a scope of your app object and use a .something of it. In other words, when you try use object name inside of object you create local variable with same name which covers global variable on time of execution of your code will be created. Overwriting global variable locally should always be avoided.

It's perfectly explained here: How do JavaScript closures work?

I agree with what @Teo said in his answer, so I refactored my code and it works now. Basically, I need one more call back to wrap my local variable and function.

Then I separated my code in function and main:

reader.js:

var reader = {
    location_command: 'M,0\rU,18\rGR\rG,LOCATE,04,0,1,3\rO,2,30,0,0\rR,8\rS,2\rM,433\r',
    tagLocation_callback: "",

    getTagLocation: function(tagId,timeout,callback) {
        console.log('in getTagLocation func')
        reader.tagLocation_callback = callback;
        reader.writeCommand(reader.location_command);
        // Subscribe Raw Data
        bluetoothSerial.subscribe(
            '\r',
            reader.onListeningData,
            function(error){
                console.log(error);
        });
    },

    // Wrapper of bluetooth write, read reponse immediately
    // after sending commands
    writeCommand: function(command) {
        console.log('sending %s', command[0]);
        bluetoothSerial.write(command,
            function(data) {
                console.log('sent data %s',data);
            }, function(error){
                console.log(error);
        });
    },

    // Listening to data, function will be continuous invoked
    // until there's no more incoming data or unsubscribe/disconnect
    // appears
    onListeningData: function(data) {
        var outputTagJson = reader.onParsing(data);
        reader.tagLocation_callback(outputTagJson);
    },

    /*
     Processor part:
     Parsing Response String and out a JSON of each Attributes
     */
    onParsing: function(data){
        data = data.slice(0,data.length-1);
        var alphabeticRegex = /^[A-Z]+$/i;          // Response header start with an alphabetic string
        var decimalRegex = /^\d+\.?\d*$/;
        var outputTagJson = {};
        if (data == null || data == '\r')           // Sanity Check
        {
            return;
        }
        Element = data.split(',');
        while (true)                                // For some response, it didn't start with 'A-z'
        {
            if (alphabeticRegex.test(Element[0]) != true)
            {
                Element.splice(0,1);
            }
            else
            {
                break;
            }
        }
        // Parsing each options, not generic, have to deal with each specific case.
        switch (Element[0]) {
            case 'VV':
                outputTagJson.readerIdentifier = {};
                outputTagJson.readerIdentifier.buildID = Element[2];
                outputTagJson.readerIdentifier.modelID = Element[3];
                break;
            case 'V':
                outputTagJson.Battery = {};
                outputTagJson.Battery.percentage = Element[2];
                if (Element[3] != null)
                {
                    outputTagJson.Battery.chargingmode = "Charging";
                }
                else
                {
                    outputTagJson.Battery.chargingmode = "Not charged";
                }
                break;
            case 'U':
                outputTagJson.timeStamp = {};
                outputTagJson.timeStamp.range = 10;
                outputTagJson.timeStamp.range = 1.00;
                break;
            case 'M':
                outputTagJson.readerMode = {};
                if (Element[1] == 0) outputTagJson.readerMode.message = 'Standby';
                if (Element[1] == 20) outputTagJson.readerMode.message = '303MHz';
                if (Element[1] == 40) outputTagJson.readerMode.message = 'Japanese 303MHz';
                if (Element[1] == 433) outputTagJson.readerMode.message = '433MHz';
                else outputTagJson.readerMode.message = 'In motion';
                break;
            case 'H':
                var len = Element.length;
                var props = ['tagID','groupID','payload','timeStamp','sigStrenthA','sigStrenthB'];
                outputTagJson.Tag = {};
                if (len >= 2)
                {
                    outputTagJson.Tag[props[0]] = Element[1];
                    outputTagJson.Tag.isLost = "False";
                    for(var i=3;i<=len;i++)
                        outputTagJson.Tag[props[i-2]] = Element[i-1].slice(1,Element[i-1].length);
                }
                break;
            case 'L':
                var len = Element.length;
                var props = ['tagID','groupID','payload','timeStamp'];
                outputTagJson.Tag = {};
                if (len >= 2)
                {
                    outputTagJson.Tag[props[0]] = Element[1];
                    outputTagJson.Tag.isLost = "True";
                    for(var i=3;i<=len;i++)
                        outputTagJson.Tag[props[i-2]] = Element[i-1].slice(1,Element[i-1].length);
                }
                break;
            case 'E':
                outputTagJson.Error = {};
                if (Element[1] == 2) outputTagJson.Error.message = "Received command is invalid";
                if (Element[1] == 3) outputTagJson.Error.message = "Command has too many parameters";
                if (Element[1] == 4) outputTagJson.Error.message = "A command parameter is out of range";
                if (Element[1] == 5) outputTagJson.Error.message = "Maximum number of group codes is exceeded";
                if (Element[1] == 6) outputTagJson.Error.message = "Character buffer is full. One or more tag reports were dropped";
                if (Element[1] == 7) outputTagJson.Error.message = "DSP offline. Indicates DSP failure or failed upgrade of DSP firmware";
                if (Element[1] == 8) outputTagJson.Error.message = "Access denied. Indicates security is enabled and a valid ':login' is required";
                break;
            default:
                break;
        }
        return outputTagJson;
    },

    unsubscribeTagLocation: function(callback){
        console.log("in unsubscribeTagLocation function ");
        bluetoothSerial.unsubscribe(
            function(data){
                console.log(data);
                callback("{message: 'tag location unsubscribed successfully!'}")
            },
            function(error) {
                console.log(error);
            });
    },
};

app.js:

var app = {
    macAddress: "00:0B:CE:01:A4:D7",  // get your mac address from bluetoothSerial.list
    //00:0B:CE:01:A4:D7
    //"00:0B:CE:07:36:AB"
    responseHeader: [''],
    timeList: [],

    /*
     Application constructor
     */
    initialize: function() {
        this.bindEvents();
        console.log("Starting SimpleSerial app");
    },
    /*
     bind any events that are required on startup to listeners:
     */
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
        connectButton.addEventListener('touchend', app.Connect, false);
        scanButton.addEventListener('touchend', app.disCover, false);
    },

    /*
     this runs when the device is ready for user interaction:
     */
    onDeviceReady: function() {
        // check to see if Bluetooth is turned on.
        // this function is called only
        //if isEnabled(), below, returns success:

        var listPorts = function() {
            // list the available BT ports:
            bluetoothSerial.list(
                function(results) {
                    app.display(JSON.stringify(results));
                },
                function(error) {
                    app.display(JSON.stringify(error));
                }
            );
        };

        // if isEnabled returns failure, this function is called:
        var notEnabled = function() {
            app.display("Bluetooth is not enabled.")
        };

        // check if Bluetooth is on:
        bluetoothSerial.isEnabled(
            listPorts,
            notEnabled
        );
    },

    disCover: function() {
        var scan = function () {
            app.clear();
            app.display("Scanning available Devices...");

            bluetoothSerial.discoverUnpaired(
                function(results) {
                    app.display(JSON.stringify(results));
                },
                function(error) {
                    app.display(JSON.stringify(error));
                }
            );
        };

        var notEnabled = function() {
            app.display("Bluetooth is not enabled.")
        };

        bluetoothSerial.isEnabled(
            scan,
            notEnabled
        );

    },

    /*
     Connects if not connected, and disconnects if connected:
     */
    Connect: function() {

        // connect() will get called only if isConnected() (below)
        // returns failure. In other words, if not connected, then connect:
        var connect = function () {
            // if not connected, do this:
            // clear the screen and display an attempt to connect
            app.clear();
            app.display("Attempting to connect. " +
                "Make sure the serial port is open on the target device.");

            // attempt to connect:
            bluetoothSerial.connect(
                app.macAddress,  // device to connect to
                app.connectSuccess,    // start listening if you succeed
                app.showError    // show the error if you fail
            );
        };

        // disconnect() will get called only if isConnected() (below)
        // returns success  In other words, if  connected, then disconnect:
        var disconnect = function () {
            app.display("attempting to disconnect");
            // if connected, do this:
            bluetoothSerial.disconnect(
                app.closePort,     // stop listening to the port
                app.showError      // show the error if you fail
            );
        };

        // here's the real action of the manageConnection function:
        bluetoothSerial.isConnected(disconnect, connect);
    },
    /*
     subscribes to a Bluetooth serial listener for newline
     and changes the button:
     */

    /*getBatteryInfo: function() {
        var Bcomm = 'WB\r';
        app.writeCommand(Bcomm);
        if (JSON.stringify(app.outputTagJson) != "{}")
        {
            bluetoothSerial.unsubscribe(function(data) {
                    app.display(JSON.stringify(app.outputTagJson));
                },
                function(error) {
                    console.log(error);
            });
            return;
        }
        bluetoothSerial.subscribe(
            '\r',
            reader.onListeningData,
            function(error){
                console.log(error);
        });
    },*/

    displayResult: function(data){

        app.display("output message : ");
        app.display(JSON.stringify(data));
    },

    // On Connection Success, we want to display battery info
    // and send command immediately
    connectSuccess: function() {
        console.log("in connectSuccess");
        app.display("Connected to: " + app.macAddress);
        connectButton.innerHTML = "Disconnect";
        reader.getTagLocation('00198525',10000, app.displayResult);
        setTimeout(function(){
            reader.unsubscribeTagLocation(app.displayResult);
        }, 5000);

    },


    /*
     unsubscribes from any Bluetooth serial listener and changes the button:
     */
    closePort: function() {
        app.clear();
        // if you get a good Bluetooth serial connection:
        app.display("Disconnected from: " + app.macAddress);
        // change the button's name:
        connectButton.innerHTML = "Connect";
        // unsubscribe from listening:
        bluetoothSerial.unsubscribe(
            function (data) {
                app.display(data);
            },
            app.showError
        );
    },
    /*
     appends @error to the message div:
     */
    showError: function(error) {
        app.display(error);
    },

    /*
     appends @message to the message div:
     */
    display: function(message) {
        var display = document.getElementById("message"), // the message div
            lineBreak = document.createElement("p"),     // a line break
            label = document.createTextNode(message);     // create the label

        display.appendChild(lineBreak);          // add a line break
        display.appendChild(label);              // add the message node
    },
    /*
     clears the message div:
     */
    clear: function() {
        var display = document.getElementById("message");
        display.innerHTML = "";
    }
};      // end of app

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM