Help for Savonia Measurements

WCF | JSON | Objects

Measurements service - WCF

You can write and read measurements via WCF service at address MeasurementsService.svc. Use this when your client is web service capable (e.g. C# or Java), which can read a WSDL file and create a client for you.
Walkthrough: Creating a simple WCF Service in Windows Forms - Accessing the Service

C# example for using the client to write measurements.


public void SendMeasurementPackage()
{
    // init the client which is created for you. Note the class name might be different!
    MeasurementsServiceClient client = new MeasurementsServiceClient();
    // create the measurement package and populate it
    MeasurementPackage package = new MeasurementPackage();
    package.Key = "your-key-goes-here";
    // create a collection of measurements
    List<MeasurementModel> measurements = new List<MeasurementModel>();
    // create a measurement
    MeasurementModel m = new MeasurementModel()
    {
        Object = "your-measurement-object",
        Tag = "your-measurement-tag",
        Timestamp = DateTimeOffset.Now,
        // if TimestampISO8601 is set then that value is used as timestamp and Timestamp property is ignored
        TimestampISO8601 = "2017-06-06 12:46:00",
        // location can be null, it is not required
        Location = new Location() 
        {
            Latitude = 62.8989, // use DD, decimal degrees, latitude -90 to 90
            Longitude = 27.6630 // use DD, decimal degrees, longitude -180 to 180
        }
    };
    // add some data to your measurement
    DataModel d = new DataModel()
    {
        Tag = "your-data-tag",
        Value = 3.14
    };
    // add data to measurement
    m.Data = new DataModel[1];
    m.Data[0] = d;
    // add measurement to measurements collection
    measurements.Add(m);
    // add measurements collection to measurement package
    package.Measurements = measurements.ToArray();
    // save the measurement package and check the result
    SaveResult result = client.SaveMeasurementPackage(package);
    // check result
    if (result.Success)
    {
        // data was saved successfully!
    }
}

C# example for using the client to read measurements.


public void ReadMeasurementPackage()
{
    // init the client which is created for you. Note the class name might be different!
    MeasurementsServiceClient client = new MeasurementsServiceClient();
    // create the measurement query
    MeasurementQueryModel package = new MeasurementQueryModel();
    query.Key = "your-key-goes-here";
    query.Obj = "measurement-object-here"; // use measurement object and measurement tag to make the query more efficient
    query.Tag = "measurement-tag-here";
    query.From = DateTime.Now.AddHours(-1); // can be set, not required
    query.To = DateTime.Now; // can be set, not required
    query.Take = 30; // can be set, not required
    // if none of the above are set the service returns 20 latest measurement from the given key

    // read measurements.
    //Note that the service might timeout when trying to read too many measurements
    var measurements = client.GetMeasurements(query);

    // print the values to console
    foreach (var item in measurements)
    {
        Console.WriteLine("\t{0}/{1} @ {2}", item.Object, item.Tag, item.Timestamp);
        foreach (var d in item.Data)
        {
            Console.WriteLine("\t\t{0} = {1}", d.Tag, d.Value);
        }
    }
}
    

Measurement service - JSON interface

CORS has been enabled to allow cross-origin HTTP requests.
Measurements service has a json interface which can be accessed from urls:

To save measurements via json post request

/json/measurements/save takes two "parameters": a key and an array of measurement objects

POST https://sami.savonia.fi/Service/3.0/MeasurementsService.svc/json/measurements/save HTTP/1.1
User-Agent: Fiddler
Host: sami.savonia.fi
Content-Type: text/json
        
{
    "key":"your-key-goes-here",
    "measurements":
    [
        {
            "Data":
            [
                {
                    "BinaryValue":[1,2,33,255],
                    "BinaryValueBase64":null,
                    "LongValue":null,
                    "Tag":"This is your sensor identification. This is unique to you. Tag max length is 50 characters.",
                    "TextValue":"your sensor can have some textual values also.",
                    "Value":3.14,
                    "XmlValue":null
                }
            ],
            "Location":{"Latitude":62.8989,"Longitude":27.6630},
            "Note":"if this measurement has a note",
            "Object":"your-measurement-object, Object max length is 50 characters.",
            "Tag":"your-measurement-tag, Tag max length is 50 characters.",
            "Timestamp":{"DateTime":"\/Date(1412878153764)\/","OffsetMinutes":180},
            "TimestampISO8601": "2017-01-10 15:30:00"
        }
    ]
}


<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(function () {
    var writeMeasurement = function () {
        var key = 'your-key-here';
        var url = 'https://sami.savonia.fi/Service/3.0/MeasurementsService.svc/json/measurements/save';
        var measurementPackageContent = {
            "key": key,
            "measurements": [
                {
                    "Data": [
                        {
                            "Tag": "json test",
                            "Value": 3.14
                        }
                    ],
                    "Object": "json cors test",
                    "Tag": "json cors tag",
                    "TimestampISO8601": "2017-01-10 12:30:00"
                }
            ]
        }
        $.ajax({
            type: 'POST',
            url: url,
            contentType: "application/json; charset=utf-8",
            dataType: 'json',
            data: JSON.stringify(measurementPackageContent)
        })
        .done(function (data) {
            // post request succeeded, check the result
            if (data.Success) {
                // data was saved!
            }
        })
        .fail(function (jqXHR, textStatus, error) {
            // something went horribly wrong...
        });
    }
});
</script>

Note 1! TimestampISO8601 or Timestamp must be present. If both timestamps are present the value in TimestampISO8601 wins.
Note 2! It's probably easier to use the TimestampISO8601 for measurement time (http://www.w3.org/TR/NOTE-datetime).
Note 3! Javascript date value Date(1412878153764) in the samples abowe equals 9.10.2014 21:09:13 when presented as Finnish (FLE Standard Time) datetime.
Note 4! Binary data can be send in Data with properties BinaryValue or BinaryValueBase64. When both are present and the value in BinaryValueBase64 is valid base64 encoded string then that value wins.
Note 5! Binary data property BinaryValue must be array of unsigned byte values i.e. array of values in range [0...255].
Note 6! Location uses DD, decimal degree notation. Valid values are: latitude -90 to 90 and longitude -180 to 180.

More info about DateTime parsing is in Serializing Dates in JSON.

To read measurements via json get request


GET https://sami.savonia.fi/Service/3.0/MeasurementsService.svc/json/measurements/your-key-here?obj=your-meas-object&tag=your-meas-tag&data-tags=comma-separated-list-of-data-tags&from=from&to=to&take=20&inclusiveFrom=true&inclusiveTo=true&binaryValueFormat=ByteArray HTTP/1.1
User-Agent: Fiddler
Host: sami.savonia.fi
Content-Type: text/json


<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(function () {
    var readMeasurements = function () {
        var key = 'your-key-here';
        var urlBase = 'https://sami.savonia.fi/Service/3.0/MeasurementsService.svc/json/measurements/';
        var url = urlBase + key;
        // you can append query parameters to url if needed
        url = url + '?obj=your-meas-object&tag=your-meas-tag&data-tags=comma-separated-list-of-data-tags&from=from&to=to&take=20&inclusiveFrom=true&inclusiveTo=true&binaryValueFormat=ByteArray';
        $.getJSON(url, null, function (data) {
            var receivedData = JSON.stringify(data);
        });
    }
});
</script>

Note 1! For parameters from and to use YYYY-MM-DD format. Example: from=2014-10-23. This is also known as ISO 8601 format http://www.w3.org/TR/NOTE-datetime.
Note 2! Parameters from and to may also contain time value in YYYY-MM-DDTHH:mm:ss format. Example: from=2014-10-23T12:45:00. If time portion is not provided then it is set to 0:00:00 before database search.
Note 3! key parameter is required, other parameters are optional.
Note 4! If both from and to parameters are submitted then take can be omitted. Otherwise take will have default value of 20.
 


if using jQuery and jsonp add callback=? query parameter as seen in http://www.bendewey.com/index.php/186/using-jsonp-with-wcf-and-jquery


<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(function () {
    var readMeasurements = function () {
        var key = 'your-key-here';
        var urlBase = 'https://sami.savonia.fi/Service/3.0/MeasurementsService.svc/json/measurements/';
        var url = urlBase + key;
        // you can append query parameters to url if needed
        url = url + '?obj=your-meas-object&tag=your-meas-tag&data-tags=comma-separated-list-of-data-tags&from=from&to=to&take=20&inclusiveFrom=true&inclusiveTo=true&binaryValueFormat=ByteArray';
        // add callback to url
        url = url + '?callback=?';
        $.getJSON(url, null, function (data) {
            var receivedData = JSON.stringify(data);
        });
    }
});
</script>

To get, add or update and delete sensor information via json

    
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(function () {
    // get sensors
    var getSensors = function () {
        var key = 'your-read-key';
        var url = 'https://sami.savonia.fi/Service/3.0/MeasurementsService.svc/json/sensors/' + key;
        $.getJSON(url, null, function (data) {
            var sensorInfo = JSON.stringify(data, null, 4));
        });
    };
});
</script>

    
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(function () {
    // add / update sensor
    var saveSensor = function () {
        var tag = 'your-sensor-tag';
        var key = 'your-modify-key';
        var url = 'https://sami.savonia.fi/Service/3.0/MeasurementsService.svc/json/sensors/' + key + '/' + tag;
        var sensorData = {};
        sensorData = JSON.parse($('#sensor-data').val()); // get sensor data from somewhere
        $.ajax({
            type: 'PUT',
            url: url,
            contentType: "application/json; charset=utf-8",
            dataType: 'json',
            data: JSON.stringify(sensorData)
        })
        .done(function (data) {
            content.html('Success: ' + JSON.stringify(data));
        })
        .fail(function (jqXHR, textStatus, error) {
            content.html('PUT error: ' + jqXHR + textStatus + error);
        });
    };
});
</script>

    
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(function () {
    // delete sensor
    var deleteSensor = function () {
        var tag = 'your-sensor-tag';
        var key = 'your-delete-key';
        var url = 'https://sami.savonia.fi/Service/3.0/MeasurementsService.svc/json/sensors/' + key + '/' + tag;
        $.ajax({
            type: 'DELETE',
            url: url,
            contentType: "application/json; charset=utf-8",
            dataType: 'json'
        })
        .done(function (data) {
            content.html('Success: ' + JSON.stringify(data));
        })
        .fail(function (jqXHR, textStatus, error) {
            content.html('DELETE error: ' + jqXHR + textStatus + error);
        });
    };
});
</script>


Objects

Both WCF and JSON interfaces uses the same object models.

MeasurementModel C# class


public class MeasurementModel
{   
    // measurement object: can be used to identify the measurement, not required
    public string Object { get; set; }
    
    // measurement tag: can be used to further identify the measurement, not required
    public string Tag { get; set; }
    
    // measurement time
    public DateTimeOffset Timestamp { get; set; }
    
    // or use the ISO8601 formatted string for the measurement time.
    // If this is set then Timestamp property's value is ignored
    public string TimestampISO8601 { get; set; }
    
    // a measurement can have a not, not required    
    public string Note { get; set; }
    
    // a measurement can have a location object, not required
    public Location Location { get; set; }
    
    // a measurement must have some data!
    public List<DataModel> Data { get; set; }
}

DataModel C# class


public class DataModel
{
    // data tag: Data must have a tag which uniquelly identifies the data or the sensor
    public string Tag { get; set; }
    
    // Value for the data, this is usually used to save the sensor value     
    public double? Value { get; set; }
    
    // a data can have other type values or they can be set to null or not provided at all
    // a data can have an integer value, not required
    public long? LongValue { get; set; }
    
    // a data can have a text value, not required
    public string TextValue { get; set; }
    
    // a data can have binary value, not required
    public byte[] BinaryValue { get; set; }
    
    // a data can have xml value, not required
    public string XmlValue { get; set; }
    
    // a data can have binary value in base64 encoded string format, not required
    // If this is set with a valid base64 encoded string then BinaryValue property is ignored.
    public string BinaryValueBase64 { get; set; }
}

Location C# class


public class Location
{
    // location latitude as decimal degree value -90 to 90 and longitude -180 to 180
    public double? Latitude { get; set; }
    
    // location longitude as decimal degree value -180 to 180
    public double? Longitude { get; set; }
}

MeasurementPackage C# class


public class MeasurementPackage
{
    // your key
    public string Key { get; set; }
    
    // a list of measurements
    public List<MeasurementModel> Measurements { get; set; }
}

SensorModel C# class


public class SensorModel
{
    public int ProviderID { get; set; }

    // sensor Tag identified the same data as DataModel.Tag. This value is used to map sensor info to data.
    public string Tag { get; set; }

    // Name for the sensor
    public string Name { get; set; }

    // a sensor can have a description
    public string Description { get; set; }

    // sensor unit e.g. m/s, kWh etc.
    public string Unit { get; set; }

    // ValueDecimalCount can be used when displaying data to round values to given number of decimals or significant numbers
    public int? ValueDecimalCount { get; set; }

    // ValidRangeMin can indicate the expected minimum valid value for the sensor
    public double? ValidRangeMin { get; set; }

    // ValidRangeMax can indicate the expected maximum valid value for the sensor
    public double? ValidRangeMax { get; set; }

    // a sensor can have a location
    public Location Location { get; set; }
}