/// Version 1.0.0
/// Last modified 23.5.2014
///
/// Copyright (C) 2014 Veli-Mikko Puupponen
///
/// Halyri-system is a prototype emergency call system. Its purpose is to
/// demonstrate the use of the advanced capabilities available in the current
/// generation smartphones in facilitating the emergency service dispatcher's
/// capability to determine the nature of the emergency and to dispatch help.
///
/// For more information, see the README file of this package.
///
/// The MIT License (MIT)
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
///
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SimpleUdpMediaClient.Packets
{
/// Veli-Mikko Puupponen
///
/// The class represents a control packet for the protocol defined
/// and implement by the UdpMediaClientSocket. The packet
/// consist of a packet type identifier, a sender GUID,
/// a Int64 sequence number, a target GUID and a command.
///
/// Control packets are primarily user to request server packet
/// routing from one client to another.
///
/// This packet can have optional payload data.
///
public class ControlPacket : INetworkPacket
{
///
/// The enumeration for command type. It is used to specify the command of the control packet.
///
public enum CommandType : byte { RequestEnableRouting, RequestDisableRouting, Statistics }
///
/// The specified id of the packet type. It is used to identify a packet.
///
public const byte PacketId = 2;
///
/// The specified length of the packet header in octets.
///
public const int HeaderLengthInOctets = 42;
private const int packetIdHeaderIndex = 0;
private const int sourceGuidHeaderIndex = 1;
private const int sequenceHeaderIndex = 17;
private const int targetGuidHeaderIndex = 25;
private const int commandHeaderIndex = 41;
private byte[] sourceGuid;
///
/// The function to get the Guid identifying the sender.
///
public string SourceGuid
{
get { return (sourceGuid == null || sourceGuid.Length != 16) ? "" : new Guid(sourceGuid).ToString(); }
}
private byte[] targetGuid;
///
/// The function to get the Guid identifying the target.
///
public string TargetGuid
{
get { return (targetGuid == null || targetGuid.Length != 16) ? "" : new Guid(targetGuid).ToString(); }
}
private Int64 sequence;
///
/// The function to set and get the packet sequence number.
///
public Int64 Sequence
{
get { return sequence; }
set { sequence = value; }
}
private byte command;
///
/// The function to get the packet command.
///
public CommandType Command
{
get { return (CommandType)command; }
}
private byte[] payloadData;
///
/// The function to get the packet payload data. Can be null.
///
public byte[] PayloadData
{
get { return payloadData; }
}
///
/// The function initializes a new ControlPacket instance with no parameters.
///
public ControlPacket() { }
///
/// The function initializes a new ControlPacket instance that has the
/// specified source and target Guids, command, payload data
/// and sequence number.
///
/// It throws an ArgumentException if source or target Guid is malformed.
///
/// The string representation of the sender's Guid.
/// The string representation of the target's Guid.
/// The packet command.
/// The optional payload data.
/// Packet sequence number.
public ControlPacket(string sourceGuid, string targetGuid, CommandType command, byte[] payload, Int64 sequenceNumber)
: this(sourceGuid, targetGuid, command, payload)
{
this.sequence = sequenceNumber;
}
///
/// The function initializes a new ControlPacket instance that has the
/// specified source and target Guids, command and sequence number.
///
/// It throws an ArgumentException if source or target Guid is malformed.
///
/// The string representation of the sender's Guid.
/// The string representation of the target's Guid.
/// The packet command.
/// Packet sequence number.
public ControlPacket(string sourceGuid, string targetGuid, CommandType command, Int64 sequenceNumber)
: this(sourceGuid, targetGuid, command)
{
this.sequence = sequenceNumber;
}
///
/// The function initializes a new ControlPacket instance that has the
/// specified source and target Guids, command and payload data.
///
/// It throws an ArgumentException if source or target Guid is malformed.
///
/// The string representation of the sender's Guid.
/// The string representation of the target's Guid.
/// The packet command.
/// The optional payload data.
public ControlPacket(string sourceGuid, string targetGuid, CommandType command, byte[] payload)
: this(sourceGuid, targetGuid, command)
{
payloadData = payload;
}
///
/// The function initializes a new ControlPacket instance that has the
/// specified source and target Guids and command.
///
/// It throws an ArgumentException if source or target Guid is malformed.
///
/// The byte representation of the sender's Guid.
/// The byte representation of the target's Guid.
/// The packet command.
public ControlPacket(string sourceGuid, string targetGuid, CommandType command)
{
Guid source;
if (!Guid.TryParse(sourceGuid, out source))
throw new ArgumentException("Guid format error on source");
this.sourceGuid = source.ToByteArray();
Guid target;
if (!Guid.TryParse(targetGuid, out target))
throw new ArgumentException("Guid format error on target");
this.targetGuid = target.ToByteArray();
this.command = (byte)command;
}
///
/// The function initializes a new ControlPacket instance that has the
/// specified source and target Guids, command and payload data.
///
/// It throws an ArgumentException if any of the arguments is null.
///
/// The byte representation of the sender's Guid.
/// The byte representation of the target's Guid.
/// The packet command.
/// The optional payload data.
public ControlPacket(byte[] sourceGuid, byte[] targetGuid, CommandType command, byte[] payload)
{
if (sourceGuid == null || targetGuid == null || payload == null)
throw new ArgumentException("Guid or payload cannot be null");
this.sourceGuid = sourceGuid;
this.targetGuid = targetGuid;
this.command = (byte)command;
payloadData = payload;
}
///
/// The function gets the command, the target guid and the packet control data from the given data and saves them.
///
/// The given data.
public void FromBytes(byte[] packetBytes)
{
if (packetBytes.Length < HeaderLengthInOctets)
throw new ArgumentException("Data length insufficient for headers.");
if(packetBytes[packetIdHeaderIndex] != PacketId)
throw new ArgumentException("Incompatible packet type.");
byte[] sourceGuidBytes = new byte[sequenceHeaderIndex - sourceGuidHeaderIndex];
Array.Copy(packetBytes, sourceGuidHeaderIndex, sourceGuidBytes, 0, sourceGuidBytes.Length);
sourceGuid = sourceGuidBytes;
sequence = BitConverter.ToInt64(packetBytes, sequenceHeaderIndex);
byte[] targetGuidBytes = new byte[commandHeaderIndex - targetGuidHeaderIndex];
Array.Copy(packetBytes, targetGuidHeaderIndex, targetGuidBytes, 0, targetGuidBytes.Length);
targetGuid = targetGuidBytes;
command = packetBytes[commandHeaderIndex];
if (packetBytes.Length > HeaderLengthInOctets)
{
payloadData = new byte[packetBytes.Length - HeaderLengthInOctets];
Array.Copy(packetBytes, HeaderLengthInOctets, payloadData, 0, payloadData.Length);
}
}
///
/// The function saves the given sequence number and returns the data header of the currently saved sequence.
///
/// The data header of current the sequence.
public byte[] GetBytes(long sequenceNumber)
{
sequence = sequenceNumber;
return GetBytes();
}
///
/// The function returns the data header of the currently saved sequence.
///
/// The data header of current the sequence.
public byte[] GetBytes()
{
int payloadLength = (payloadData == null) ? 0 : payloadData.Length;
byte[] networkPacketData = new byte[HeaderLengthInOctets + payloadLength];
networkPacketData[packetIdHeaderIndex] = PacketId;
Array.Copy(sourceGuid, 0, networkPacketData, sourceGuidHeaderIndex, sourceGuid.Length);
byte[] sequenceBytes = BitConverter.GetBytes(sequence);
Array.Copy(sequenceBytes, 0, networkPacketData, sequenceHeaderIndex, sequenceBytes.Length);
Array.Copy(targetGuid, 0, networkPacketData, targetGuidHeaderIndex, targetGuid.Length);
networkPacketData[commandHeaderIndex] = command;
if (payloadLength > 0)
Array.Copy(payloadData, 0, networkPacketData, HeaderLengthInOctets, payloadData.Length);
return networkPacketData;
}
}
}