/// <summary> /// Add time-series data to the channel. /// </summary> /// <param name="sensorName">sensor containing the channel to add data to.</param> /// <param name="channelName">channel to add data to.</param> /// <param name="sampleRate">sample-rate for points in data</param> /// <param name="data">a list of points to add to the channel. /// <remarks>The points should be ordered and match the sample-rate provided.</remarks> /// </param> public void AddTimeSeriesData(SampleRate sampleRate, IEnumerable <Point> data) { var payload = new MemoryStream(); var xdr = new XdrWriter(payload); const int VERSION = 1; xdr.WriteInt(VERSION); xdr.WriteInt(sampleRate.Type.ToXdr()); xdr.WriteInt(sampleRate.Rate); //Writing an array in XDR. an array is always prefixed by the array length xdr.WriteInt(data.Count()); foreach (Point p in data) { xdr.WriteUnsingedHyper(p.UnixTimestamp); xdr.WriteFloat(p.Value); } string url = "/sensors/" + _sensorName + "/channels/" + this.Name + "/streams/timeseries/data/"; var request = _requests.url(url) .Param("version", "1") .ContentType("application/xdr") .Data(payload.ToArray()) .Put(); // check the response code for success if (request.ResponseCode != HttpStatusCode.Created) { throw SensorCloudException.GenerateSensorCloudException(request, "AddTimeSeriesData failed."); } }
/// <summary> /// Add a new sensor to the Device. /// </summary> /// <param name="sensorName">Name that will be given to the sensor. Once a sensor is created it's name cannot be changed. /// <remarks>sensorName must be between 1 and 50 characters in length, and only contain characters in the set[A-Z,a-z,0-9,_]</remarks> /// </param> /// <param name="label">User Friendly label for the sensor. May be changed later. /// <remarks>label supports unicode, and must not be more than 50 bytes when encoded as UTF-8.</remarks> /// </param> /// <param name="type">Type of the sensor. May be changed later. /// <remarks>type supports unicode, and must not be more than 50 bytes when encoded as UTF-8.</remarks> /// </param> /// <param name="description">Description of the sensor. May be changed later. /// <remarks>description supports unicode, and must not be more than 1000 bytes when encoded as UTF-8.</remarks> /// </param> public Sensor AddSensor(String sensorName, String label, String type, String description) { var payload = new MemoryStream(); var xdr = new XdrWriter(payload); int VERSION = 1; xdr.WriteInt(VERSION); xdr.WriteString(type); xdr.WriteString(label); xdr.WriteString(description); var request = _requests.url("/sensors/" + sensorName + "/") .Param("version", "1") .ContentType("application/xdr") .Data(payload.ToArray()) .Put(); // check the response code for success if (request.ResponseCode != HttpStatusCode.Created) { throw SensorCloudException.GenerateSensorCloudException(request, "AddSensor Failed"); } return(new Sensor(sensorName, _requests)); }
// Save type, label, and descritpion to SensorCloud private void SaveState(string label, string description, string type) { var payload = new MemoryStream(); var xdr = new XdrWriter(payload); const int VERSION = 1; xdr.WriteInt(VERSION); xdr.WriteString(type); xdr.WriteString(label); xdr.WriteString(description); var request = _requests.url("/sensors/" + this.Name + "/") .Param("version", "1") .Accept("application/xdr") .ContentType("application/xdr") .Data(payload.ToArray()) .Post(); if (request.ResponseCode != HttpStatusCode.Created) { throw SensorCloudException.GenerateSensorCloudException(request, "Save Sensor state failed."); } }
private IList <Point> DownloadData(ulong startTime, ulong endTime) { //url: /sensors/<sensor_name>/channels/<channel_name>/streams/timeseries/data/ // params: // starttime (required) // endtime (required) // showSampleRateBoundary (oiptional) // samplerate (oiptional) string url = "/sensors/" + _sensorName + "/channels/" + this._channelName + "/streams/timeseries/data/"; var request = _requests.url(url) .Param("version", "1") .Param("starttime", startTime) .Param("endtime", endTime) .Accept("application/xdr") .Get(); // check the response code for success if (request.ResponseCode == HttpStatusCode.NotFound) { //404 is an empty list return(new List <Point>()); } else if (request.ResponseCode != HttpStatusCode.OK) { //all other errors are exceptions throw SensorCloudException.GenerateSensorCloudException(request, "AddTimeSeriesData failed."); } var xdrReader = new XdrReader(request.Raw); var datapoints = new List <Point>(); ulong timestamp; float value; try { // timeseries/data always returns a relativly small chunk of data less than 50,000 points so we can proccess it all at once. We won't be given an infinite stream //however a futre enhancemnts could be to stat proccessing this stream as soon as we have any bytes avialable to the user, so they could be //iterating over the data while it is still being downloaded. while (true) { timestamp = xdrReader.ReadUnsingedHyper(); value = xdrReader.ReadFloat(); datapoints.Add(new Point(timestamp, value)); } } catch (EndOfStreamException) {} return(datapoints); }
/// <summary> /// Delete a channel from the Sensor. Deleteing a channel will delete all the data that is assosiated with the channel. /// </summary> /// <param name="channelName"></param> public void DeleteChannel(string channelName) { var request = _requests.url("/sensors/" + this.Name + "/channels/" + channelName + "/") .Param("version", "1") .Delete(); if (request.ResponseCode != HttpStatusCode.NoContent) { throw SensorCloudException.GenerateSensorCloudException(request, "Delete channel Failed"); } }
/// <summary> /// Determine if a sensor exists for this Device. /// </summary> /// <param name="sensorName"></param> /// <returns>True if the sensor exists.</returns> public bool HasSensor(String sensorName) { var request = _requests.url("/sensors/" + sensorName + "/") .Param("version", "1") .Accept("application/xdr") .Get(); // check the response code for success switch (request.ResponseCode) { case HttpStatusCode.OK: return(true); case HttpStatusCode.NotFound: return(false); default: throw SensorCloudException.GenerateSensorCloudException(request, "HasSensor failed"); } }
/// <summary> /// Determine if a channel for a specific sensor exits for the Device. /// </summary> /// <param name="sensorName"></param> /// <param name="channelName"></param> /// <returns>True if the channel exists</returns> /// <exception cref="SensorCloudException">Unexpected response from server.</exception> public bool HasChannel(string channelName) { //make a get request for channel attributes to determine if the channel exists String url = "/sensors/" + this.Name + "/channels/" + channelName + "/attributes/"; var request = _requests.url(url) .Param("version", "1") .Accept("application/xdr") .Get(); // check the response code for success switch (request.ResponseCode) { case HttpStatusCode.OK: return(true); case HttpStatusCode.NotFound: return(false); default: throw SensorCloudException.GenerateSensorCloudException(request, "Unexpected response for HasChannel."); } }
/// <summary> /// Load info about this sensor from SensorCloud /// If this sensor doesn't exist then an exception will be thrown /// </summary> private void LoadFromSensorCloud() { if (_infoLoaded) { return; } //make a get request for channel attributes to determine if the channel exists var request = _requests.url("/sensors/" + this.Name + "/") .Param("version", "1") .Accept("application/xdr") .Get(); // check the response code for success if (request.ResponseCode == HttpStatusCode.OK) { var ms = new MemoryStream(request.Raw); var xdr = new XdrReader(ms); int version = xdr.ReadInt(); if (version != 1) { throw new SensorCloudException("Unsupported xdr version"); } string type = xdr.ReadString(1000); string label = xdr.ReadString(1000); //a token is generally about 60 chars. Limit the read to at most 1000 chars as a precation so in their is a protocol error we don't try to allocate lots of memory string descripiton = xdr.ReadString(1000); //Only if we get all the fields from xdr do we update this instance. This prevents us from paritally updating the state of the instance. _label = label; _description = descripiton; _sensorType = type; } else { throw SensorCloudException.GenerateSensorCloudException(request, "Get Sensor Failed"); } _infoLoaded = true; }
/// <summary> /// Get a list of existing Sensors for this device. /// </summary> public List <Sensor> GetSensors() { List <Sensor> outputList = new List <Sensor>(); //make a get request for channel attributes to determine if the channel exists var request = _requests.url("/sensors/") .Param("version", "1") .Accept("application/xdr") .Get(); // check the response code for success if (request.ResponseCode == HttpStatusCode.OK) { var ms = new MemoryStream(request.Raw); var xdr = new XdrReader(ms); //The first value is the version. We expect this to be 1 int version = xdr.ReadInt(); if (version != 1) { throw new SensorCloudException("Unsupported xdr version"); } //The next value is an integer telling us how many sensors to unpack //Each sensor has its info packed a certain way, so this allows us to know how many sensors we must unpack int sensorCount = xdr.ReadInt(); //loop through sensors for (var i = 0; i < sensorCount; i++) { string sensorName = xdr.ReadString(1000); //for example's sake, I'm going to push a Sensor Object onto our output array //This object only needs the Sensor name, and will fetch the Sensor info on it's own //However, to get all sensor names we must still parse all of the stream. You can modify //this function to meet your own needs, and return a different set of information //tailored to what you need. outputList.Add(new Sensor(sensorName, _requests)); string sensortype = xdr.ReadString(1000); string sensorlabel = xdr.ReadString(1000); //a token is generally about 60 chars. Limit the read to at most 1000 chars as a precation so in their is a protocol error we don't try to allocate lots of memory string sensordescripiton = xdr.ReadString(1000); //the next value is the number of channels this sensor has int channelCount = xdr.ReadInt(); //loop all channels for (var j = 0; j < channelCount; j++) { string channelName = xdr.ReadString(1000); string channelLabel = xdr.ReadString(1000); string channelDescription = xdr.ReadString(1000); //This tells us how many datastreams the channel has //This should be one, as we only currently support TimeSeries, but could grow as more stream types are added int streamCount = xdr.ReadInt(); //loop through streams for (var k = 0; k < streamCount; k++) { //the first value in the stream is the type of stream. We expect this to be TS_V1, meaning timeseries version 1 string streamType = xdr.ReadString(1000); //the second value of a stream is the number of bytes that describes the stream. //This is convenient as it allows us to just skip the stream if we are not interested in it. //For this example's purpose, we are uninterested and will simply skip ahead in the xdr. We do this by reading the number bytes, but doing anything //with the data int totalBytes = xdr.ReadInt(); //just reading the bytes by previously parsed length advances the xdr reader forward in the data xdr.ReadBytes(totalBytes); } } } return(outputList); } else { throw SensorCloudException.GenerateSensorCloudException(request, "Get Sensors Failed"); } }