Main Page | Class Hierarchy | Class List | File List | Class Members

SmartDeviceClient/SmartProtocolStack/RemoteHost/NACKManager.cs

00001 using System;
00002 using System.Collections;
00003 using System.Threading;
00004 using GPRSWeb.SmartDeviceClient.Common;
00005 
00006 namespace GPRSWeb.SmartDeviceClient.SmartProtocolStack.RemoteHost
00007 {
00012         public class NACKManager
00013         {
00015                 private NackQueue nackQueue; // queue of NACKs waiting to be sent out
00016                 private TransmissionQueue txQueue; // the transmission queue in which to output things
00017                 private RemoteHostComms thisRemoteHost;
00018 
00020 
00021 
00022 
00023                 public NACKManager(RemoteHostComms thisRemoteHost) {
00024                         this.thisRemoteHost = thisRemoteHost;
00025                         nackQueue = new NackQueue();
00026                         this.txQueue = thisRemoteHost.TxQueue;
00027                 }
00028 
00029                 public void ResetConnections() {
00030                         Console.WriteLine("NACK Manager Reset Starting ...");
00031                         nackQueue.Clear();
00032                         Console.WriteLine("... NACK Manager Reset Complete");
00033                 }
00034 
00039                 public NackInfo GetNextNack() {
00040                         /* only one thread should be calling this at any given time, so no need to lock */
00041                         if (nackQueue.IsEmpty) { return null; }
00042                         else return nackQueue.Dequeue();
00043                 }
00044 
00045                 public void ProcessReceivedSegmentHeaders(ref SegmentHeaders newHeaders) {
00046                         /* examines headers to see which segments need to be retransmitted */
00047                         /* calculate end point of nack range 
00048                          * use it in a for loop to add a nack retransmision message to the relevant queue */
00049                         uint lastNACK;
00050                         uint firstNACK;
00051                         ushort destination;
00052                         //Console.WriteLine("NACK Manager: Process Received Segment Headers");
00053                         if (newHeaders.IsNACK) { // Retransmit
00054                                 firstNACK = newHeaders.NackInfo.SequenceNumber;
00055                                 if (newHeaders.NackInfo.IsRanged) { lastNACK = firstNACK + newHeaders.NackInfo.Range; }
00056                                 else { lastNACK = firstNACK; }
00057                                 destination = newHeaders.SourceDeviceID; // this should always be the same.
00058 
00059                                 // TODO: Fix so that range can wrap round.
00060                                 for (uint i = firstNACK; i <= lastNACK; i++) {
00061                                         // grab segment from retransmission buffer
00062                                         Segment tempSegment = thisRemoteHost.RetransmissionBuffer.GetSegment(i);
00063                                         // encapsulate segment in retransmission message
00064                                         RetransmissionMessage retransMessage = new RetransmissionMessage(tempSegment);
00065                                         // set the destination of the retransmission
00066                                         retransMessage.Destination = destination;
00067                                         // plonk message into the correct transmission queue
00068                                         TransmissionQueueEntry[] entries = TransmissionQueueEntry.FromMessage(retransMessage);
00069                                         txQueue.Enqueue(entries, false);
00070                                         Console.WriteLine("NACK Manager: Issued Reransmision of #" + i);
00071                                 }
00072                         }
00073                 }
00074 
00079                 public void IssueNACK(uint segmentNumber) {
00080                         SendNACK(new NackInfo(segmentNumber));
00081                 }
00082 
00088                 public void IssueNACK(uint segmentNumber, long range) {
00089                         for (long offset = 0; offset < range; offset++) {
00090                                 IssueNACK((uint)(segmentNumber + offset));
00091                         }
00092                         //SendNACK(new NackInfo(segmentNumber, (uint)range));
00093                 }
00094 
00095                 private void SendNACK(NackInfo nackInfo) {
00096                         lock (txQueue.SyncRoot) {
00097                                 if (txQueue.IsEmpty) {
00098                                         /* create no-op message */
00099                                         Message noopMsg = new EmptyMessage();
00100                                         noopMsg.Destination = thisRemoteHost.RemoteDeviceID;
00101                                         /* segmentise */
00102                                         TransmissionQueueEntry[] buffer = TransmissionQueueEntry.FromMessage(noopMsg);
00103                                         /* place results in segment queue */
00104                                         // make sure this all happens in a monitor, so we get them all together
00105                                         txQueue.EnqueueUnprotected(buffer, false);
00106                                 }
00107                                 nackQueue.Enqueue(nackInfo);
00108                                 Console.WriteLine("NACK Manager: NACK Sent for #" + nackInfo.SequenceNumber + " Range: " + (nackInfo.IsRanged ? nackInfo.Range.ToString() : "N/A"));
00109                                 Monitor.PulseAll(txQueue.SyncRoot);
00110                         }
00111                 }
00112         }
00113 
00114         /*****************************************************************************/
00115         public class NackInfo {
00117                 bool isRanged;
00118                 uint sequenceNumber;
00119                 uint range;
00120 
00122                 public NackInfo(uint sequenceNumber) {
00123                         this.isRanged = false;
00124                         this.sequenceNumber = sequenceNumber;
00125                 }
00126 
00127                 public NackInfo(uint sequenceNumber, uint range) {
00128                         this.isRanged = true;
00129                         this.sequenceNumber = sequenceNumber;
00130                         this.range = range;
00131                 }
00132 
00134                 public bool IsRanged { get { return isRanged; }}
00135                 public uint SequenceNumber { get { return sequenceNumber; } }
00136                 public uint Range { get { return range; } }
00137 
00138 
00139         }
00140 
00141 
00142         /*****************************************************************************/
00146         class NackQueue : Queue {
00147                 public NackQueue() : base() {}
00148 
00154                 public void Enqueue(NackInfo nackInfo) {
00155                         lock (this.SyncRoot) {
00156                                 base.Enqueue(nackInfo);                 
00157                         } 
00158                 }
00159 
00160                 public new NackInfo Dequeue() { // this must not block and wait, because of where it's tested.
00161                         NackInfo result;
00162                         lock (this.SyncRoot) {
00163                                 result = (NackInfo)base.Dequeue();
00164                         }
00165                         return result;
00166                 }
00167 
00168                 public bool IsEmpty {
00169                         get {
00170                                 bool result;
00171                                 lock (this.SyncRoot) {
00172                                         result = (this.Count == 0);
00173                                 }
00174                                 return result;
00175                         }
00176                 }
00177         }
00178 
00179         /*****************************************************************************/
00180         public class RetransmissionBuffer {
00181                 /* Contains a copy of every segment that may be requested by a 
00182                  * negative acknowledgement.
00183                  * 
00184                  * Data is indexed by segment number. Segment number and timestamp
00185                  * would have been better, but without adding another field to a
00186                  * segment we can not be sure of selecting the correct segment for
00187                  * retransmission.
00188                  * 
00189                  * If a segment is retransmitted, it will not need to be re-transmitted again
00190                  * (the segment encapsulating the retransmission will be retransmitted). So we 
00191                  * can erase the segment from the buffer when it is sent for retransmission.
00192                  * 
00193                  * If segment m is such that all segments < m have been received, and m is a NACK 
00194                  * requesting segment n, then we can retransmit 'n' and erase from the buffer
00195                  * all segments <= n. 
00196                  * 
00197                  * Let's just weaken this condition to 'if there are no missing segments'. 
00198                  * This is the case where m is the segment just received. And to be honest, 
00199                  * this is likely to be the case.
00200                  * 
00201                  * Failure case: some segment > m is carrying a NACK for segment < n. 
00202                  * This would occur if... there is no way this would occur.
00203                  * 
00204                  * Note that all segments < m must have been received, not NACKd.
00205                  * 
00206                  * As a failsafe we should have a 'Segment not in Buffer' message for problems.
00207                  * */
00209                 Hashtable buffer;
00210                 private SequenceManager seqManager;
00211                 
00213                 private uint earliestSegmentNumber {
00214                         get { 
00215                                 // TODO: Correct Min for wrapround error
00216                                 lock (buffer.SyncRoot) {
00217                                         ICollection keys = buffer.Keys;
00218                                         uint[] keyArray = new uint[keys.Count];
00219                                         keys.CopyTo(keyArray, 0);
00220                                         Array.Sort(keyArray);
00221                                         return keyArray[0];
00222                                 }
00223                         }
00224                 }
00225 
00227                 public RetransmissionBuffer(SequenceManager seqManager) {
00228                         this.seqManager = seqManager;
00229                         buffer = new Hashtable();
00230                 }
00231 
00233 
00234 
00235 
00236                 public void ResetConnection() {
00237                         lock (buffer.SyncRoot) {
00238                                 buffer.Clear();
00239                         }
00240                 }
00241 
00242 
00246                 private void Add(Segment target) {
00247                         lock (buffer.SyncRoot) {
00248                                 buffer.Add(target.Headers.SequenceNumber, target);
00249                         }
00250 
00251                 }
00252 
00256                 private void Remove(uint segmentNumber) {
00257                         lock (buffer.SyncRoot) {
00258                                 buffer.Remove(segmentNumber);
00259                         }
00260                 }
00261 
00266                 private Segment Read(uint segmentNumber) {
00267                         lock (buffer.SyncRoot) {
00268                                 return (Segment)buffer[segmentNumber];
00269                         }
00270                 }
00271 
00278                 public Segment GetSegment(uint sequenceNumber) {
00279                         lock (buffer.SyncRoot) {
00280                                 Segment result = null;
00281 
00282                                 result = Read(sequenceNumber);
00283                                 /* when we erase all segments 'less' than n, how do we identify them? They could wrap round,
00284                                  * and thus some could be > n. Perhaps we should have a linked list type index structure? */
00285                                 /* or perhaps we should have some kind of 'earliest segment' indicator. */
00286                                 if (!seqManager.IsSegmentsMissing) {
00287                                         // erase all segments less than nackSegmentHeaders.SequenceNumber
00288                                         // i.e. all the ones that were put in before it.
00289                                         if (earliestSegmentNumber > sequenceNumber) {
00290                                                 for (long i = sequenceNumber; i >= 0; i--) {
00291                                                         this.Remove((uint)i); }
00292                                                 for (long i = UInt32.MaxValue; i >= earliestSegmentNumber; i--) {
00293                                                         this.Remove((uint)i); }
00294                                         } else { // earliestSegmentNumber <= nackSegmentHeaders.SequenceNumber
00295                                                 for (long i = sequenceNumber; i >= earliestSegmentNumber; i--) {
00296                                                         this.Remove((uint)i); }
00297                                         }
00298                                 } // else  there are segments missing, so we can't erase anything for certain, so noop.
00299                                 return result;
00300                         }
00301                 }
00302 
00307                 public void PutSegment(Segment target) {
00308                         // todo: handle exceptions, etc.
00309                         lock (buffer.SyncRoot) {
00310                                 this.Add(target);
00311                         }
00312                 }
00313         
00314 
00315         }
00316 }

Generated on Mon May 8 22:07:27 2006 by  doxygen 1.3.9.1