1

I have a small bottle server that returns random values given a machineID as shown below:

@app.route('/dataMachine')
@enable_cors
def simulatedMachineData():

    prevVals = {'machineID_1': 0,'machineID_2': 0, 'machineID_3': 0,}
    devVals = {'machineID_1': 5.3,'machineID_2': 2.1,'machineID_3': 7.1,}    

    dictVals  = bottle.request.query.decode()
    machineID = dictVals.get('machine', '')
    if machineID not in devVals.keys(): return '-1'
    prevVals[machineID] += normal(scale=devVals[machineID])
    return str(prevVals[machineID])

This does not have a problem.

Now I have an AngularJS app that ties to interact with this data, as shown below:

var app = angular.module('testBidirection', []);

app.controller('tempCntrl', function ($scope, $http, $interval){

    /////////////////////////////////////////////////
    // Data to display and its update function
    $scope.data = {
        'machineID_1':'0',
        'machineID_2':'0',
        'machineID_3':'0',
    };

    var machineIDs = ['machineID_1', 'machineID_2', 'machineID_3'];
    $scope.machineID = 'machineID_1';

    $scope.getData = function(){
        for (var i = 0; i < 3; i++) {
                $scope.machineID = machineIDs[i];
                $http.get('http://localhost:8080/dataMachine?machine=' + $scope.machineID).success(function(data) {
                    $scope.data[ $scope.machineID ] = data;
                });
        };
    };


    //////////////////////////////////////////////////
    // Button controller. Use this controller to 
    //      toggle the initiation of getting and 
    //      stopping data retrieval ...
    $scope.buttonText = 'start Comm';
    $scope.toggleComm = function(){
        if ($scope.buttonText == 'start Comm') {
            $scope.buttonText = 'stop Comm';
            stop = $interval($scope.getData, 2*1000);
        } else {
            $scope.buttonText = 'start Comm';
            $interval.cancel(stop);
            stop=undefined;
        }
    };

});

I believe that the problem is within this function:

$scope.getData = function(){
    for (var i = 0; i < 3; i++) {
            $scope.machineID = machineIDs[i];
            $http.get('http://localhost:8080/dataMachine?machine=' + $scope.machineID).success(function(data) {
                $scope.data[ $scope.machineID ] = data;
            });
    };
};

When I run this script, I see only that the last machine ID changes. An example output on the browser when the script is running is the following:

{"machineID_1":"0","machineID_2":"0","machineID_3":"2.37252542925"}

The value for "machineID_3" keeps changing every 2 seconds like it should, while that for "machineID_1" amd "machineID_2" remains 0.

I can see from my Python output that the server is getting the correct get requests every 2 seconds, as in the example shown below.

127.0.0.1 - - [22/Apr/2016 16:33:32] "GET /dataMachine?machine=machineID_3 HTTP/1.1" 200 14
127.0.0.1 - - [22/Apr/2016 16:33:34] "GET /dataMachine?machine=machineID_1 HTTP/1.1" 200 14
127.0.0.1 - - [22/Apr/2016 16:33:34] "GET /dataMachine?machine=machineID_2 HTTP/1.1" 200 14
127.0.0.1 - - [22/Apr/2016 16:33:34] "GET /dataMachine?machine=machineID_3 HTTP/1.1" 200 14
127.0.0.1 - - [22/Apr/2016 16:33:36] "GET /dataMachine?machine=machineID_1 HTTP/1.1" 200 13
127.0.0.1 - - [22/Apr/2016 16:33:36] "GET /dataMachine?machine=machineID_2 HTTP/1.1" 200 14
127.0.0.1 - - [22/Apr/2016 16:33:36] "GET /dataMachine?machine=machineID_3 HTTP/1.1" 200 13

Am I missing something in the scoping rules?

Resources:

Entire contents of the Bottle Server:

import bottle
from bottle import response
from numpy.random import normal

import json
import pandas as pd 

app = bottle.Bottle()

# the decorator
def enable_cors(fn):
    def _enable_cors(*args, **kwargs):
        # set CORS headers
        response.headers['Access-Control-Allow-Origin'] = '*'
        response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS'
        response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'

        if bottle.request.method != 'OPTIONS':
            # actual request; reply with the actual response
            return fn(*args, **kwargs)

    return _enable_cors

@app.route('/dataMachine')
@enable_cors
def simulatedMachineData():

    prevVals = {'machineID_1': 0,'machineID_2': 0,'machineID_3': 0,}
    devVals = {'machineID_1': 5.3,'machineID_2': 2.1,'machineID_3': 7.1,}    

    dictVals  = bottle.request.query.decode()
    machineID = dictVals.get('machine', '')
    if machineID not in devVals.keys(): return '-1'
    prevVals[machineID] += normal(scale=devVals[machineID])
    return str(prevVals[machineID])

Entire contents of the HTML file:

<!DOCTYPE html>
<html>
<head lang="en">
    <title>Plotly Graph Plotter Directive for AngularJS - Demo</title>
    <script src="jquery.min.js"></script>
    <script src="angular.min.js"></script>
    <script src="app3.js"></script>
</head>
<body ng-app="testBidirection">

Testing the return Values ...
<div  ng-controller="tempCntrl">

    <button ng-click='toggleComm()'>{{buttonText}}</button>
    <hr>
    {{data}}
    <hr>
</div>

</body>
</html>
ssm
  • 5,277
  • 1
  • 24
  • 42
  • in the for loop.. should machineIDs[i]; be $scope.machineIDs[i];? – codeMan Apr 22 '16 at 08:57
  • I tried all variations of adding `$scope` everywhere, including `$scope.i`, Using a different form of the `for` loop (e.g. `for (machineID in $scope.data)`) etc. Nothing seems to work :( – ssm Apr 22 '16 at 09:03
  • trying adding $scope.apply() in $http.get success method – codeMan Apr 22 '16 at 09:10
  • Pardon my ignorance. Just starting with AngularJS. What is `$scope.apply()`? – ssm Apr 22 '16 at 09:12
  • I see that you have already got the solution to this problem. Nonethe less go through this link to know about $scope.$apply() http://jimhoskins.com/2012/12/17/angularjs-and-apply.html – codeMan May 02 '16 at 11:17

2 Answers2

1

I can see couple of problems in your code in the following lines.

$scope.machineID = 'machineID_1';

Why you assigned this in to $scope variable? As I can see you only use it send machine id to the server inside your loop. If you don't use this variable inside your view, this should be corrected as follows.

var machineID = 'machineID_1';

$scope.getData = function(){ for (var i = 0; i < 3; i++) { $scope.machineID = machineIDs[i]; $http.get('http://localhost:8080/dataMachine?machine=' + $scope.machineID).success(function(data) { $scope.data[ $scope.machineID ] = data; }); }; };

Send Http call to a server is asynchronous. So if you do it like above, Your code may send 3 http requests to server with machineId=3. Because, of asynchronous manner, your loop will finish the execution before all http calls send. It should be corrected as follows. And also, you assigned response of the server call as follows.

$scope.data[ $scope.machineID ] = data;

Now the $scope.machineID may be equal to 3 because of the asynchronous behavior of the http calls. So even you get data fro all 3 machine IDS, actually you assigned the values for machineId 3.

$scope.getData = function(){
    for (var i = 0; i < 3; i++) {

            $http.get('http://localhost:8080/dataMachine?machine=' + machineIDs[i]).success(function(data) {
                $scope.data[machineIDs[i]] = data;
            });
    };
};

UPDATE

    $scope.getData = function(){
       for (var i = 0; i < 3; i++) {
        var machineId=machineIDs[i];
        $http.get('http://localhost:8080/dataMachine?machine=' + machineId).success((function(machineId) {
                 return function(data) {
                 $scope.data[machineId] = data;
               }
        })(machineId));
      }
    }
Madura Pradeep
  • 2,378
  • 1
  • 30
  • 34
  • I have tried that as well. Did not work. The reason I used `$scope.machineID` is because I thought that `machineID` couldnt be accessed from within the `$http.get` request ... – ssm Apr 22 '16 at 09:05
  • If you see from the server logs, the three calls always happen. However, if I try your suggestion (which I had already tried), I `machineIDs[i]` inside the scope becomes undefined. Here is what I see on the browser ... `{"machineID_1":"0","machineID_2":"0","machineID_3":"0","undefined":"0.0299554435569"} ` – ssm Apr 22 '16 at 09:09
  • Does your server return back the machineId or only the value? – Madura Pradeep Apr 22 '16 at 09:11
  • Only a value. This is a contrived example. Basically each machine will eventually return only the current measurement value in production. – ssm Apr 22 '16 at 09:14
  • I think the easiest solution is to send machineId with the value from the server. Something like below. {"machineId":"1","value":"2"}. Because your server knows the machine Id. – Madura Pradeep Apr 22 '16 at 09:23
  • @ssm You can do it without change anything from the server side. Check my updated answer. Check following stack overflow answer for more details. http://stackoverflow.com/questions/19116815/how-to-pass-a-value-to-an-angularjs-http-success-callback – Madura Pradeep Apr 22 '16 at 09:35
  • Excellent, excellent. Now with the current modification, the program is working fine!! – ssm Apr 23 '16 at 01:50
1

Building on the answer posted by Madura Pradeep, to see the source of the problem consider the following:

$http calls are asynchronous and the success function is called only when the response is received from the server. Also HTTP requests are significantly slower than a simple for loop execution. Therefore by the time the first response is received and the success function called the loop has terminated and the loop variable i == undefined and $scope.machineID == 'machineID_3'. As a result, all three return values get assigned to $scope.data[ 'machineID_3' ]

Niko
  • 11
  • 1
  • 2