/// <summary> /// Overloaded constructor /// </summary> /// <param name="optionNumber">The option number</param> /// <param name="optionValue">The option value</param> public CoAPHeaderOption(UInt16 optionNumber, byte[] optionValue) { int maxLengthOfOptionValue = this.GetOptionValueMaxLengthInBytes(optionNumber); int minLengthOfOptionValue = this.GetOptionValueMinLengthInBytes(optionNumber); int lengthOfOptionValue = (optionValue == null) ? 0 : optionValue.Length; this.Number = optionNumber; if (this.IsRecognized()) { if (lengthOfOptionValue < minLengthOfOptionValue || lengthOfOptionValue > maxLengthOfOptionValue) { throw new CoAPFormatException("Invalid length of option value for the given option number " + optionNumber); } } if (this.Number == CoAPHeaderOption.CONTENT_FORMAT) { CoAPContentFormatOption contentFormat = new CoAPContentFormatOption(AbstractByteUtils.ToUInt16(optionValue)); if (!contentFormat.IsValid()) { throw new CoAPFormatException("Content format not supported"); } } if (optionValue != null && optionValue.Length > 0) { this.Value = new byte[optionValue.Length]; Array.Copy(optionValue, this.Value, optionValue.Length); this.ValueSizeInBytes = (UInt16)optionValue.Length; } }
/// <summary> /// Called when a response is received against a sent request /// </summary> /// <param name="coapResp">The CoAPResponse object</param> private void OnCoAPResponseReceived(CoAPResponse coapResp) { string tokenRx = (coapResp.Token != null && coapResp.Token.Value != null) ? AbstractByteUtils.ByteToStringUTF8(coapResp.Token.Value) : ""; FileLogger.Write("Received response from server - token = " + tokenRx); FileLogger.Write(coapResp.ToString()); if (tokenRx == __Token) { if (BreakIfError(coapResp.Code.Value)) { return; } if (coapResp.Code.Value == CoAPMessageCode.CONTENT) { ArrayList options = coapResp.Options.GetOptions((ushort)CoAPHeaderOption.CONTENT_FORMAT); if (options.Count > 0) { CoAPContentFormatOption ccformat = new CoAPContentFormatOption(); bool proceed = false; foreach (CoAPHeaderOption o in options) { ccformat = new CoAPContentFormatOption(AbstractByteUtils.ToUInt16(o.Value)); if (ccformat.IsValid()) { proceed = true; break; } } if (proceed) { if (ccformat.Value == CoAPContentFormatOption.TEXT_PLAIN) { string result = AbstractByteUtils.ByteToStringUTF8(coapResp.Payload.Value); __PingResult = result; } if (ccformat.Value == CoAPContentFormatOption.APPLICATION_OCTET_STREAM) { string result = HdkUtils.BytesToHexView(coapResp.Payload.Value); __PingResult = result; } if (ccformat.Value == CoAPContentFormatOption.APPLICATION_JSON) { string result = HdkUtils.BytesToHexView(coapResp.Payload.Value); __PingResult = result; } __Response = coapResp; __Done.Set(); } } } else { //Will come here if an error occurred.. } } }
///// <summary> ///// Called when a request is received... ///// </summary> ///// <param name="coapReq">The CoAPRequest object</param> //private void OnCoAPRequestReceived(CoAPRequest coapReq) //{ // Console.WriteLine(coapReq.QueryString); //} /// <summary> /// Called when a response is received against a sent request /// </summary> /// <param name="coapResp">The CoAPResponse object</param> private void OnCoAPResponseReceived(CoAPResponse coapResp) { string tokenRx = (coapResp.Token != null && coapResp.Token.Value != null) ? AbstractByteUtils.ByteToStringUTF8(coapResp.Token.Value) : ""; if (tokenRx == __Token) { if (coapResp.Code.Value == CoAPMessageCode.CONTENT) { ArrayList options = coapResp.Options.GetOptions((ushort)CoAPHeaderOption.CONTENT_FORMAT); if (options.Count > 0) { CoAPContentFormatOption ccformat = new CoAPContentFormatOption(); bool proceed = false; foreach (CoAPHeaderOption o in options) { ccformat = new CoAPContentFormatOption(AbstractByteUtils.ToUInt16(o.Value)); if (ccformat.IsValid()) { proceed = true; break; } } if (proceed) { if (ccformat.Value == CoAPContentFormatOption.TEXT_PLAIN) { string result = AbstractByteUtils.ByteToStringUTF8(coapResp.Payload.Value); Console.WriteLine("Get on Olimex " + __coapClient.EndPoint.ToString() + " = " + result); __GetResult = result; } if (ccformat.Value == CoAPContentFormatOption.APPLICATION_OCTET_STREAM) { string result = SSNUtils.Conversion.BytesToHexView(coapResp.Payload.Value); Console.WriteLine("Get on Olimex " + __coapClient.EndPoint.ToString() + " = " + result); __GetResult = result; } if (ccformat.Value == CoAPContentFormatOption.APPLICATION_JSON) { string result = SSNUtils.Conversion.BytesToHexView(coapResp.Payload.Value); Console.WriteLine("Get on Olimex " + __coapClient.EndPoint.ToString() + " = " + result); __GetResult = result; } __Response = coapResp; } } } else { //Will come here if an error occurred.. } } __Done.Set(); }
/// <summary> /// From the given byte stream, just check what is the message Id (CON/NON/ACK/RST). /// The message byte stream might be corrupted, so we only make a best case attempt /// </summary> /// <param name="data">The data stream to parse</param> /// <returns>The message type identifier</returns> public static UInt16 PeekMessageID(byte[] data) { if (data == null || data.Length < AbstractCoAPMessage.HEADER_LENGTH) { throw new ArgumentNullException("Invalid byte stream to parse"); } UInt16 mId = 0; try { byte[] mIdBytes = new byte[2]; Array.Copy(data, 2, mIdBytes, 0, 2); mId = AbstractByteUtils.ToUInt16(mIdBytes); } catch { } return(mId); }
/// <summary> /// The byte array from which we need to construct this block option. /// </summary> /// <param name="data"> /// The byte array (max 3 bytes).Endian-ness is taken care of in this method /// </param> public CoAPBlockOption(byte[] data) { if (data == null || data.Length == 0 || data.Length > 3) { throw new ArgumentNullException("Block option byte array must be 1-3 bytes in length"); } if (data.Length == 1 /*single byte*/) { this._seqNumber = (UInt16)(data[0] >> 4); this._hasMore = (((data[0] & 0x08) >> 3) == 1) ? true : false; this._sizeExp = (byte)(data[0] & 0x07); } else if (data.Length == 2 /*double byte*/) { if (AbstractByteUtils.IsTargetLittleEndian()) { this._seqNumber = (UInt16)(AbstractByteUtils.ToUInt16(data) >> 4); this._hasMore = (((data[0] & 0x08) >> 3) == 1) ? true : false; this._sizeExp = (byte)(data[0] & 0x07); } else { this._seqNumber = (UInt16)(AbstractByteUtils.ToUInt16(data) << 4); this._hasMore = (((data[1] & 0x08) >> 3) == 1) ? true : false; this._sizeExp = (byte)(data[1] & 0x07); } } else if (data.Length == 3 /*Triple byte*/) { if (AbstractByteUtils.IsTargetLittleEndian()) { this._seqNumber = (UInt32)(AbstractByteUtils.ToUInt64(data) >> 4); this._hasMore = (((data[0] & 0x08) >> 3) == 1) ? true : false; this._sizeExp = (byte)(data[0] & 0x07); } else { this._seqNumber = (UInt32)(AbstractByteUtils.ToUInt64(data) << 4); this._hasMore = (((data[2] & 0x08) >> 3) == 1) ? true : false; this._sizeExp = (byte)(data[2] & 0x07); } } }
/// <summary> /// Going forward, we will receive temperature notifications from /// server in a CON request /// </summary> /// <param name="coapReq">CoAPRequest</param> static void OnCoAPRequestReceived(CoAPRequest coapReq) { if (coapReq.MessageType.Value == CoAPMessageType.CON) { //Extract the temperature..but first, check if the notification is fresh //The server sends a 4-digit sequence number int newObsSeq = AbstractByteUtils.ToUInt16(coapReq.Options.GetOption(CoAPHeaderOption.OBSERVE).Value); if ((lastObsSeq < newObsSeq && ((newObsSeq - lastObsSeq) < (System.Math.Pow(2.0, 23.0)))) || (lastObsSeq > newObsSeq && ((lastObsSeq - newObsSeq) > (System.Math.Pow(2.0, 23.0)))) || DateTime.Today > lastObsRx.AddSeconds(128)) { //The value received from server is new....read the new temperature //We got the temperature..it will be in payload in JSON string payload = AbstractByteUtils.ByteToStringUTF8(coapReq.Payload.Value); Hashtable keyVal = JSONResult.FromJSON(payload); int temp = Convert.ToInt32(keyVal["temp"].ToString()); //do something with the temperature now Debug.WriteLine(coapReq.ToString()); } //update how many notifications received countOfNotifications++; if (countOfNotifications > 5) { //We are no longer interested...send RST to de-register CoAPResponse resp = new CoAPResponse(CoAPMessageType.RST, CoAPMessageCode.EMPTY, coapReq.ID.Value); resp.RemoteSender = coapReq.RemoteSender; resp.Token = coapReq.Token;//Do not forget this...this is how messages are correlated coapClient.Send(resp); } else { //we are still interested...send ACK CoAPResponse resp = new CoAPResponse(CoAPMessageType.ACK, CoAPMessageCode.EMPTY, coapReq.ID.Value); resp.RemoteSender = coapReq.RemoteSender; resp.Token = coapReq.Token;//Do not forget this...this is how messages are correlated coapClient.Send(resp); } lastObsSeq = newObsSeq; lastObsRx = DateTime.Today; } }
/// <summary> /// Parse the CoAP message stream and extract message Id (in network byte order) /// </summary> /// <param name="coapMsgStream">The CoAP message stream that contains the token length and value</param> /// <param name="startIndex">The index to start looking for the value</param> /// <param name="extraInfo">Not used</param> /// <returns>The next index from where next set of information can be extracted from the message stream</returns> public int Parse(byte[] coapMsgStream, int startIndex, UInt16 extraInfo) { if (coapMsgStream == null || coapMsgStream.Length == 0 || startIndex < 0) { return(startIndex); //do nothing } if (coapMsgStream.Length < AbstractCoAPMessage.HEADER_LENGTH) { throw new CoAPFormatException("Invalid CoAP message stream"); } if (startIndex >= coapMsgStream.Length) { throw new ArgumentException("Start index beyond message stream length"); } //We read two bytes... byte[] mid = { coapMsgStream[startIndex], coapMsgStream[startIndex + 1] }; //We received them in network byte order...fix the order based on the platform mid = AbstractNetworkUtils.FromNetworkByteOrder(mid); Value = AbstractByteUtils.ToUInt16(mid); return(startIndex + 2); }
/// <summary> /// Get the request URL /// </summary> /// <returns>The coap URL associated with the request</returns> public string GetURL() { string scheme = (this.IsSecure) ? "coaps" : "coap"; string host = "localhost"; UInt16 port = 5683; if (this.Options.HasOption(CoAPHeaderOption.URI_HOST)) { host = AbstractByteUtils.ByteToStringUTF8(this.Options.GetOption(CoAPHeaderOption.URI_HOST).Value); } if (this.Options.HasOption(CoAPHeaderOption.URI_PORT)) { port = AbstractByteUtils.ToUInt16(this.Options.GetOption(CoAPHeaderOption.URI_PORT).Value); } ; string path = this.GetPath(); string qString = this.QueryString; string url = scheme + "://" + host + ":" + port; url += (path != null && path.Trim().Length > 0) ? "/" + path : ""; url += (qString != null && qString.Trim().Length > 0) ? "?" + qString : ""; return(url); }
/// <summary> /// Called when a response is received against a sent request /// </summary> /// <param name="coapResp">The CoAPResponse object</param> private void OnCoAPResponseReceived(CoAPResponse coapResp) { string tokenRx = (coapResp.Token != null && coapResp.Token.Value != null) ? AbstractByteUtils.ByteToStringUTF8(coapResp.Token.Value) : ""; if (tokenRx == __Token) { if (coapResp.Code.Value == CoAPMessageCode.CONTENT) { ArrayList options = coapResp.Options.GetOptions((ushort)CoAPHeaderOption.CONTENT_FORMAT); if (options.Count > 0) { bool proceed = false; foreach (CoAPHeaderOption o in options) { CoAPContentFormatOption ccformat = new CoAPContentFormatOption(AbstractByteUtils.ToUInt16(o.Value)); if (ccformat.Value == CoAPContentFormatOption.APPLICATION_LINK_FORMAT) { proceed = true; } } if (proceed) { string nodes = ""; if (coapResp.Payload != null) { nodes = AbstractByteUtils.ByteToStringUTF8(coapResp.Payload.Value); } Console.WriteLine("Find Nodes response " + __coapClient.EndPoint.ToString() + " = " + nodes); __FindResult = nodes; } } } else { //Will come here if an error occurred.. } } __Done.Set(); }
/// <summary> /// Convert to a string representation /// </summary> /// <returns>string</returns> public override string ToString() { string optionValueAsString = (this.Value != null) ? AbstractByteUtils.ByteToStringUTF8(this.Value) : ""; switch (this.Number) { case CoAPHeaderOption.ACCEPT: return("Accept : " + AbstractByteUtils.ToUInt16(this.Value).ToString()); case CoAPHeaderOption.BLOCK1: CoAPBlockOption cbo1 = new CoAPBlockOption(this.Value); return("Block1 : " + cbo1.ToString()); case CoAPHeaderOption.BLOCK2: CoAPBlockOption cbo2 = new CoAPBlockOption(this.Value); return("Block2 : " + cbo2.ToString()); case CoAPHeaderOption.CONTENT_FORMAT: CoAPContentFormatOption ccformat = new CoAPContentFormatOption(AbstractByteUtils.ToUInt16(this.Value)); return("Content-Format : " + ccformat.ToString()); case CoAPHeaderOption.ETAG: return("ETag : " + optionValueAsString); case CoAPHeaderOption.IF_MATCH: return("If-Match : " + optionValueAsString); case CoAPHeaderOption.IF_NONE_MATCH: return("If-None-Match : "); case CoAPHeaderOption.OBSERVE: return("Observe : " + AbstractByteUtils.ToUInt64(this.Value).ToString()); //We have no data structure for 3-bytes case CoAPHeaderOption.LOCATION_PATH: return("Location-Path : " + optionValueAsString); case CoAPHeaderOption.LOCATION_QUERY: return("Location-Query : " + optionValueAsString); case CoAPHeaderOption.MAX_AGE: return("Max-Age : " + AbstractByteUtils.ToUInt64(this.Value).ToString()); case CoAPHeaderOption.PROXY_SCHEME: return("Proxy-Scheme : " + optionValueAsString); case CoAPHeaderOption.PROXY_URI: return("Proxy-Uri : " + optionValueAsString); case CoAPHeaderOption.SIZE1: return("Size1 : " + AbstractByteUtils.ToUInt64(this.Value).ToString()); case CoAPHeaderOption.SIZE2: return("Size2 : " + AbstractByteUtils.ToUInt64(this.Value).ToString()); case CoAPHeaderOption.URI_HOST: return("Uri-Host : " + optionValueAsString); case CoAPHeaderOption.URI_PATH: return("Uri-Path : " + optionValueAsString); case CoAPHeaderOption.URI_PORT: return("Uri-Port : " + AbstractByteUtils.ToUInt16(this.Value)); case CoAPHeaderOption.URI_QUERY: return("Uri-Query : " + optionValueAsString); default: return("Unknown : " + optionValueAsString); } }
/// <summary> /// Create a header option by parsing the coap data stream /// </summary> /// <param name="coapStream">The CoAP message stream that contains the option</param> /// <param name="startIndex">The index to start looking for the option</param> /// <param name="previousOptionNumber">The previous option number in the sequence of options</param> /// <returns>The next index from where next set of information can be extracted from the message stream</returns> public int Parse(byte[] coapStream, int startIndex, UInt16 previousOptionNumber) { if (coapStream == null || coapStream.Length < startIndex || startIndex < AbstractCoAPMessage.HEADER_LENGTH) { throw new ArgumentNullException("CoAP stream is null or start index is invalid"); } int nextIndex = startIndex; byte optionDelta = (byte)((coapStream[nextIndex] & 0xF0) >> 4); byte optionLength = (byte)(coapStream[nextIndex] & 0x0F); UInt16 optionStreamSizeInBytes = 1; //Calculate option number if (optionDelta < 13) { this.Number = (UInt16)(optionDelta + previousOptionNumber); } else if (optionDelta == 13) { this.Number = (UInt16)((UInt16)coapStream[++nextIndex] + 13 + previousOptionNumber); optionStreamSizeInBytes += 1; } else if (optionDelta == 14) { byte[] optionDeltaEx = new byte[] { coapStream[++nextIndex], coapStream[++nextIndex] }; optionDeltaEx = AbstractNetworkUtils.FromNetworkByteOrder(optionDeltaEx); this.Number = (UInt16)(AbstractByteUtils.ToUInt16(optionDeltaEx) + (UInt16)269 + previousOptionNumber); optionStreamSizeInBytes += 2; } else if (optionDelta == 15) { throw new CoAPFormatException("Option delta cannot contain payload marker value"); } //Calculate option value length if (optionLength < 13) { ValueSizeInBytes = optionLength; } else if (optionLength == 13) { ValueSizeInBytes = (UInt16)(coapStream[++nextIndex] + 13); optionStreamSizeInBytes += 1; } else if (optionLength == 14) { byte[] optionLengthEx = new byte[] { coapStream[++nextIndex], coapStream[++nextIndex] }; optionLengthEx = AbstractNetworkUtils.FromNetworkByteOrder(optionLengthEx); ValueSizeInBytes = (UInt16)(AbstractByteUtils.ToUInt16(optionLengthEx) + (UInt16)269); optionStreamSizeInBytes += 2; } else if (optionLength == 15) { throw new CoAPFormatException("Option length cannot be 0x0F. This is a reserved value"); } //Check if option is of type uint or is empty if ((this.NeedsByteOrdering(this.Number) && optionLength == 0) || this.IsEmpty()) { //this means that the value of the option is zero or this is an empty option Value = null; } else if (ValueSizeInBytes > 0) //normal processing { //Get option Value Value = new byte[ValueSizeInBytes]; Array.Copy(coapStream, ++nextIndex, Value, 0, ValueSizeInBytes); if (this.NeedsByteOrdering(this.Number)) { Value = AbstractNetworkUtils.FromNetworkByteOrder(Value); } optionStreamSizeInBytes += ValueSizeInBytes; } return(startIndex + optionStreamSizeInBytes); }
public void SetGetResult(byte[] payload) { string discovery = AbstractByteUtils.ByteToStringUTF8(payload); Console.WriteLine("Discovery = " + discovery); __GetResult = discovery; __InternalPayload = payload; CoAPResponse coapResp = new CoAPResponse(); try { coapResp.FromByteStream(payload); string tokenRx = (coapResp.Token != null && coapResp.Token.Value != null) ? AbstractByteUtils.ByteToStringUTF8(coapResp.Token.Value) : ""; if (tokenRx == __Token) { if (coapResp.Code.Value == CoAPMessageCode.CONTENT) { ArrayList options = coapResp.Options.GetOptions((ushort)CoAPHeaderOption.CONTENT_FORMAT); if (options.Count > 0) { CoAPContentFormatOption ccformat = new CoAPContentFormatOption(); bool proceed = false; foreach (CoAPHeaderOption o in options) { ccformat = new CoAPContentFormatOption(AbstractByteUtils.ToUInt16(o.Value)); if (ccformat.IsValid()) { proceed = true; break; } } if (proceed) { if (ccformat.Value == CoAPContentFormatOption.TEXT_PLAIN) { string result = AbstractByteUtils.ByteToStringUTF8(coapResp.Payload.Value); Console.WriteLine("Get " + " = " + result); __GetResult = result; } if (ccformat.Value == CoAPContentFormatOption.APPLICATION_OCTET_STREAM) { string result = HdkUtils.BytesToHexView(coapResp.Payload.Value); Console.WriteLine("Get " + " = " + result); __GetResult = result; } if (ccformat.Value == CoAPContentFormatOption.APPLICATION_JSON) { string result = HdkUtils.BytesToHexView(coapResp.Payload.Value); Console.WriteLine("Get " + " = " + result); __GetResult = result; } __Response = coapResp; } } } else { //Will come here if an error occurred.. } } } catch { } }
/// <summary> /// Called when a response is received against a sent request /// </summary> /// <param name="coapResp">The CoAPResponse object</param> private void OnCoAPDiscoveryResponseReceived(CoAPResponse coapResp) { __TimedOut = false; string tokenRx = "";// (coapResp.Token != null && coapResp.Token.Value != null) ? AbstractByteUtils.ByteToStringUTF8(coapResp.Token.Value) : ""; if (coapResp != null) { if (coapResp.Token != null) { tokenRx = AbstractByteUtils.ByteToStringUTF8(coapResp.Token.Value); } } try { FileLogger.Write("Received response from server - token = " + tokenRx); FileLogger.Write(coapResp.ToString()); } catch { } if (tokenRx == __Token) { if (BreakIfError(coapResp.Code.Value)) { this.ErrorResult = coapResp.Code.ToString(); return; } if (coapResp.Code.Value == CoAPMessageCode.CONTENT) { // Looks like we have the expected response. // Check the content format and figure out how to store the response. ArrayList options = coapResp.Options.GetOptions((ushort)CoAPHeaderOption.CONTENT_FORMAT); if (options.Count > 0) { CoAPContentFormatOption ccformat = new CoAPContentFormatOption(); bool proceed = false; foreach (CoAPHeaderOption o in options) { ccformat = new CoAPContentFormatOption(AbstractByteUtils.ToUInt16(o.Value)); if (ccformat.IsValid()) { proceed = true; break; } } // The header options match what we expected. // Set the discovery result. if (proceed) { if (ccformat.Value == CoAPContentFormatOption.APPLICATION_LINK_FORMAT) { string discovery = AbstractByteUtils.ByteToStringUTF8(coapResp.Payload.Value); __DiscoveryResult = discovery; } __Response = coapResp; __Done.Set(); } } } else { //Will come here if an error occurred.. } } }