---
# System prepended metadata

title: Use CancellationTokenSource design the rs232 wait read mechanism
tags: [Refactor, Clen Code, CSharp]

---

###### tags: `Clen Code`  `Refactor` `CSharp`

# Use CancellationTokenSource design the rs232 wait read mechanism

近期須做一些RS232讀取設備相關專案，寫了一個下完指令等待讀取的方法，特別寫一篇[Use Delgate to Clean Code Record](https://hackmd.io/@spyua/r11q8hY7c)。

跟朋友分享過後，他提出其實用CancellationTokenSource就可以達到設計等待讀取時間這件事情。

## 原先程式碼


``` csharp = 
public async Task<double> GetCrossTLK()
{
    var tokenSouce = new CancellationTokenSource();
    CancellationToken ct = tokenSouce.Token;
    // Send Command
    Com.DiscardInBuffer();
    Com.Write("GTLK\r");
    var readBuffer = new List<byte>();
    SpinWait.SpinUntil(() => false, 50);
    try
    {
    // Read Task
        var readTask = Task.Run(() =>
        {
            ct.ThrowIfCancellationRequested();
            var endTagIndex = 0;
            while (endTagIndex <= 0)
            {
                byte[] readBufftemp = new byte[Com.BytesToRead];
                Com.Read(readBufftemp, 0, Com.BytesToRead);
                readBuffer.AddRange(readBufftemp);
                Array.Clear(readBufftemp, 0, readBufftemp.Length);
                endTagIndex = Array.IndexOf(readBuffer.ToArray(), RS232EndTag);

                if (ct.IsCancellationRequested)
                {
                    ct.ThrowIfCancellationRequested();
                }
            }
        }, tokenSouce.Token);

        // Timeout
        if (await Task.WhenAny(readTask, Task.Delay(5000)) == readTask)
        {
            // Parase
            var value = readBuffer.ToArray();
            var trkValue = Encoding.UTF8.GetString(value);
            var tlkValue = Convert.ToDouble(trkValue) * -0.01;
            tokenSouce.Cancel();
            return tlkValue;
        }
        else
        {
            tokenSouce.Cancel();
            throw new Exception("Read Time Out!");
        }
    }
    catch
    {
        throw;
    }
}
```

上述程式碼可看到使用CancellationTokenSource去手動取消Task，而偵測是否Read逾時這件事情則是用另一個Task Delay去實作。

但實際CancellationTokenSource是可以設定Token的有效時間，只要在New Instance時注入TimeSpan或是毫秒Delay時間。

![](https://i.imgur.com/sFy39F2.png)


 當注入限制時間數字後，Token除了可以使用Cancel去取消外，時間一到Token就會設為已過期狀態。
 
 ## Clean Code
 
 在原本的程式碼我們在New Cancel Token時帶入時間設置。
 
```csharp = 

  var token = new CancellationTokenSource(TimeSpan.FromMilliseconds(5000));

```

接著更改我們開始讀取的While條件，已Token是否逾期為判定，而讀取時如果讀到結尾符則取消讀取迴圈進入解析區段

```csharp = 

while (!token.IsCancellationRequested)
{

    var readBufftemp = new byte[Com.BytesToRead];
    Com.Read(readBufftemp, 0, Com.BytesToRead);
    readBuffer.AddRange(readBufftemp);
    Array.Clear(readBufftemp, 0, readBufftemp.Length);
    if (Array.IndexOf(readBuffer.ToArray(), RS232EndTag) > 0)
        break;
}

 // Timeout
if (token.IsCancellationRequested)
{
    throw new Exception("Read Time Out!");
}
// Parase

```
這樣撰寫我們就可以完全省掉原本用Task Delay去做逾時機制的程式碼區段，整段處理完後會如下

```csharp = 

public async Task<double> GetCrossTLK()
{
    var token = new CancellationTokenSource(TimeSpan.FromMilliseconds(5000));

    // Send Command
    Com.DiscardInBuffer();
    Com.Write("GTLK\r");
    
    var readBuffer = new List<byte>();
    await Task.Delay(TimeSpan.FromMilliseconds(50));
    
    try
    {
    // Read Task
        var readTask = Task.Run(() =>
        {
            ct.ThrowIfCancellationRequested();
        
            while (!token.IsCancellationRequested)
            {

                var readBufftemp = new byte[Com.BytesToRead];
                Com.Read(readBufftemp, 0, Com.BytesToRead);
                readBuffer.AddRange(readBufftemp);
                Array.Clear(readBufftemp, 0, readBufftemp.Length);
                if (Array.IndexOf(readBuffer.ToArray(), RS232EndTag) > 0)
                    break;
            }

            // Timeout
            if (token.IsCancellationRequested)
            {
                throw new Exception("Read Time Out!");
            }
            // Parase
            var value = readBuffer.ToArray();
            var trkValue = Encoding.UTF8.GetString(value);
            var tlkValue = Convert.ToDouble(trkValue) * -0.01;
            return tlkValue; 
    }
    catch
    {
        throw;
    }
}
```