Skip to Content

In this second part of my posts about the Run tracker we built as an IoT demo I’ll explain how the XS app helps us transform the data received from the mobile device into Odata and JSON.

Structure of XS package

[JW5] SAP HANA Editor - Google Chrome.jpg

What we need for the Fiori app

  • Provide the app with a list of sessions so the user can choose with session to show
  • Provide a formatted string with the route of the runner, so the OpenStreetMap can visualise this.
  • Provide a service with the data of heart beat, running speed and corresponding time.
  • A service that allows the Fiori app to send messages to the runner’s device

The first service we created is the service to expose the two most important tables: the table containing the start of each session, and the table containing all the sensor data. Although later on we won’t use the raw sensor data as it would be too intensive for the Fiori app to do calculations with the sensor data.

So, here is “sensordata.odata”:


service {
  "NEO_428NLYZHYKK7NQxxxxxxxBU3I"."T_IOT_7DA1E3E315AE6AAxxxxx" as "sensordata";
  "NEO_428NLYZHYKK7NQxxxxxxxBU3I"."T_IOT_C9FB5B5B3AF5138xxxxx" as "sessiondata";
  "_SYS_BIC"."s000xxxxxxtrial.main.sapience_techday.data::users.deviceUser" as "deviceuser";
}






Pretty simple, right? XS allows us to expose tables directly as Odata service.

Next step is to provide our app with a json string representing the different GPS points that should be plotted on the map in the Fiori app. This is quite a quick & dirty approach and doesn’t adhere to any standard. When queried it will just return a string of gps points linked to a session id in a format that can be directly passed to the Open Street map.

route.xsjs:


$.response.contentType = "application/json";
$.response.status = 200;
$.response.contentType = "text/plain";
try {
    switch ( $.request.method ) {
        //Handle your GET calls here
        case $.net.http.GET:
            $.response.setBody(JSON.stringify(fnHandleGet($.request.parameters.get('sessionid'))));
            break;
        //Handle your PUT calls here
        case $.net.http.PUT:
            $.response.setBody(JSON.stringify(fnHandlePut()));
            break;    
        default:
            break;
    }
} catch (err) {
    $.response.setBody("Failed to execute action: " + err.toString());
}
//Implementation of GET call
function fnHandleGet(sessionid) {
    var oConnection = $.db.getConnection();
    var sBody = "";
    var sQuery = "SELECT C_LONGITUDE, C_LATITUDE, C_ALTITUDE FROM NEO_v428NLYZHYKK7NQxxxxxxxBU3I.T_IOT_7DA1E3E315AE6AAxxxx where C_SESSIONID = ? order by C_TIMESTAMP DESC";
    var oStatement = oConnection.prepareStatement(sQuery);
    oStatement.setString(1, sessionid);
    var oResultSet = oStatement.executeQuery();
    while (oResultSet.next()) {
        var latitude = oResultSet.getNString(1);
        var longitude = oResultSet.getNString(2);
        var altitude = oResultSet.getNString(3);
        if(latitude !== "0" && longitude !== "0")
            sBody += latitude + ";" + longitude + ";" + altitude + ";";
    }
    sBody = sBody.substring(0, sBody.length -1);
    oResultSet.close();
    oStatement.close();
    return {"route":sBody};
}
//Implementation of PUT call
function fnHandlePut() {
    return {"myStatus":"success"};
}






So basicly the app passes the session id and this XS app will return the route in a json string.

This service provides the app with data about heart beat and speed. This data will be shown in a 2D graph.

heartbeat.xsjs


$.response.contentType = "application/json";
$.response.status = 200;
$.response.contentType = "text/plain";
try {
    switch ( $.request.method ) {
        //Handle your GET calls here
        case $.net.http.GET:
            $.response.setBody(JSON.stringify(fnHandleGet($.request.parameters.get('sessionid'))));
            break;
        //Handle your PUT calls here
        case $.net.http.PUT:
            $.response.setBody(JSON.stringify(fnHandlePut()));
            break;       
        default:
            break;
    }
} catch (err) {
    $.response.setBody("Failed to execute action: " + err.toString());
}
//Implementation of GET call
function fnHandleGet(sessionid) {
    var oConnection = $.db.getConnection();
    var sQuery = "SELECT C_TIMESTAMP, C_HEARTBEAT, C_SPEED FROM NEO_v428NLYZHYKK7NQxxxxxxxBU3I.T_IOT_7DA1E3E315AE6AA9xxxx where C_SESSIONID = ? order by C_TIMESTAMP ASC";
    var oStatement = oConnection.prepareStatement(sQuery);
    oStatement.setString(1, sessionid);
    var oResultSet = oStatement.executeQuery();
    var jsonarray = [];
    while (oResultSet.next()) {
        var timestamp = Number(oResultSet.getNString(1));
        var heartbeat = Number(oResultSet.getNString(2));
        var speed = Number(oResultSet.getNString(3));
   
        if(heartbeat !== "0") {
            //sBody += '{"time":' + timestamp + ",\"heartreate\":" + heartbeat + "}";
            jsonarray.push({"timestamp": timestamp,
                            "heartrate": heartbeat,
                            "speed": speed});
       
    
        }
    }
    oResultSet.close();
    oStatement.close();
    return jsonarray;
}
//Implementation of PUT call
function fnHandlePut() {
    return {"myStatus":"success"};
}



The last service allows the app to post messages to the runner’s device

push.xsjs


$.response.contentType = "application/json";
$.response.status = 200;
if($.request.method === $.net.http.POST) {
    var data = JSON.parse($.request.body.asString());
    var oConnection = $.db.getConnection();
 
    var sQuery = "INSERT INTO NEO_v428NLYZHYKK7NQxxxxxxxBU3I.T_IOT_HTTP_PUSH VALUES (?,?,?)";
 
    var oStatement = oConnection.prepareStatement(sQuery);
 
    oStatement.setString(1, data.device);
    oStatement.setString(2, new Date(data.timestamp).toISOString());
    oStatement.setString(3, JSON.stringify(data.message));
 
    var result = oStatement.executeQuery();
 
    oConnection.commit();
    oConnection.close();
 
    $.response.setBody(JSON.stringify({"result": "ok"}));
}

This service pushes messages right into the push message table of the IOT MMS. The Android app polls this service every few seconds. Of course this isn’t an ideal scenario and the Mobile Services part of HCP is much more fit to do this part.

So this was an overview of our XS layer. Of course, with the new SPS 11 we can use XS Advanced, based on Node.js. Something I’m more acquainted with… So I promise the next solution will be much cleaner ;-).

Until the next time!

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

Leave a Reply