在建立了通訊協定最上層的類別後,開始來處理發送部份的協定。
在建立了通訊協定最上層的類別後,開始來處理發送部份的協定。
回到 [Serial Port 系列(15) 基本篇 -- 發送回應(一)]看一下我們要倒底要發送些什麼?基本上就是三件事:
(1) 讀取某一個區塊 (Block) 的資料
(2) 寫入資料到某一個區塊 (Block)
(3) 開關燈號
這樣分解後表示至少有以上三個 Method ,因為我們要取得的是最終要傳送出去的 Byte 型別陣列,所以這三個 Method 的回傳值都將會是 Byte 陣列。接著來考慮這三個 Method 所需的傳入參數:
(1) 讀取某一個區塊 (Block) 的資料
(1-1) 參數1:要讀取的 block 編號,型別 Byte,值限制在 0~63
(2) 寫入資料到某一個區塊 (Block)
(2-1) 參數1:要寫入的 block 編號,型別 Byte,值限制在 0~63
(2-2) 參數2:要寫入的值,型別 Byte 陣列,陣列長度必須等於 16
(3) 開關燈號
(3-2) 參數1:開燈或關燈,型別 Boolean,True 代表開燈 / False 代表關燈
回到前一篇所畫的流程圖,以上三個方法都會走過一樣的流程,這表示它們之間必然有幾個地方是會共用的。
(1) 第一個會共用的 Method 是藉由資料內容的長度計算整個陣列長度與長度碼,由於整個陣列長度在這個協定下的規定永遠等於長度碼的值加 2,所以要建立一個計算長度碼的 Method,其參數為資料內容的 Byte 陣列,回傳值則為 Byte 型別的長度碼
(2) 第二個會共用的 Method 是計算 XOR 值的方法,不過因為這個已經先寫在上層的類別了,所以只要繼承就可以了。
(3) 第三個會共用的 Method 是把整個通訊內容串起來成為一個可發送的 Byte 陣列,也就是把開頭碼、長度碼 …XOR碼全塞進一個陣列的 Method ,考慮一下這個 Method 會有哪些東西是不固定的,主要是長度碼、命令碼、資料內容與XOR值,其中長度碼與XOR值已經有了其它 Method 計算,因此傳入參數只要有資料內容的Byte 陣列與命令碼。
到這邊程式其實就差不多等於是寫完了,然後以實際程式碼來說明步驟:
宣告類別並繼承
Public Class WriteProtocol
Inherits Protocol
class WriteProtocol : Protocol
Method GetLengthValue :計算長度碼
Private Function GetLengthValue(ByVal dataBytes As Byte()) As Byte
If Not dataBytes Is Nothing Then
Return Convert.ToByte(dataBytes.Length + 3)
Else
Return 3
End If
End Function
private Byte GetLengthValue(Byte[] dataBytes)
{
if (dataBytes != null)
{
return ((Byte)(dataBytes.Length + 3));
}
else
{ return (Byte)3; }
}
長度碼的值 = 接收者(1 Byte) + 命令 (1Byte) + 資料內容長度 + XOR (1 Byte),所以其值是資料內容的長度加 3。
Method GetFullBytes :組合出可以用於發送的Byte 陣列
Private Function GetFullBytes(ByVal dataBytes As Byte(), ByVal command As CommandCode) As Byte()
Dim lengthValue As Byte = GetLengthValue(dataBytes)
Dim result(lengthValue + 1) As Byte
result(0) = Head
result(1) = lengthValue
result(2) = Direction.PCToDevice
result(3) = command
If lengthValue > 3 Then
Array.Copy(dataBytes, 0, result, 4, dataBytes.Length)
End If
result(result.Length - 1) = GetXorValue(result)
Return result
End Function
private Byte[] GetFullBytes(Byte[] dataBytes, CommandCode command)
{
Byte lengthValue = GetLengthValue(dataBytes);
Byte[] result = new Byte[(Int32)lengthValue + 2];
result[0] = Head;
result[1] = lengthValue;
result[2] = (Byte)Direction.PCToDevice;
result[3] = (Byte)(command);
if (lengthValue > 3)
{
Array.Copy(dataBytes, 0, result, 4, dataBytes.Length);
}
result[result.Length - 1] = GetXorValue(result);
return result;
}
在此方法內先呼叫 GetLengthValue 取得長度碼,並依此長度宣告要發送的陣列大小,請注意由於 Visual Baisc 和 C# 在陣列宣告的方式有點不同,因此 Visual Basic 是宣告索引上限所以加 1,C# 則是宣告長度所以加 2。
Method GetLightControlBytes:產生燈號控制的 Byte 陣列內容
Public Function GetLightControlBytes(ByVal LightOn As Boolean) As Byte()
Return GetFullBytes(New Byte() {Convert.ToByte(LightOn)}, CommandCode.LightControl)
End Function
public Byte[] GetLightControlBytes(Boolean LightOn)
{
return GetFullBytes(new Byte[] { Convert.ToByte(LightOn) }, CommandCode.LightControl);
}
Method GetReadBlockBytes:產生讀取特定 block 資料的 Byte 陣列內容
Public Function GetReadBlockBytes(ByVal block As Byte) As Byte()
If block <= 63 Then
Return GetFullBytes(New Byte() {block}, CommandCode.ReadBlock)
Else
Throw New ArgumentOutOfRangeException
End If
End Function
public Byte[] GetReadBlockBytes(Byte block)
{
if (block <= 63)
{
return GetFullBytes(new Byte[] { block }, CommandCode.ReadBlock );
}
else
{
throw new ArgumentOutOfRangeException();
}
}
Method GetWriteBlockBytes:產生寫入資料到特定 block 的 Byte 陣列內容
Public Function GetWriteBlockBytes(ByVal block As Byte, ByVal blockData As Byte()) As Byte()
If block <= 63 AndAlso blockData.Length = 16 Then
Dim dataBytes(blockData.Length) As Byte
dataBytes(0) = block
Array.Copy(blockData, 0, dataBytes, 1, blockData.Length)
Return GetFullBytes(dataBytes, CommandCode.WriteBlock)
Else
Throw New ArgumentOutOfRangeException()
End If
End Function
public Byte[] GetWriteBlockBytes(Byte block, Byte[] blockData)
{
if (block <= 63 && blockData .Length ==16)
{
Byte[] dataBytes = new Byte[blockData.Length + 1];
dataBytes[0] = block;
Array.Copy(blockData, 0, dataBytes, 1, blockData.Length);
return GetFullBytes(dataBytes, CommandCode.WriteBlock);
}
else
{
throw new ArgumentOutOfRangeException();
}
}
命令發送的部份這樣就完成了,因此其它的類別只要產生這個執行個體,呼叫要使用的方法並傳入正確的參數就能獲取的要發送的完整陣列內容。