[C#.NET][TCP Socket] 利用 Socket 控制 Advantech RU25 Reader-Active Mode
續上篇,[C#.Net][TCP Socket] 利用 Socket 控制 Advantech RU25 Reader–Passive Mode,我已經可以對 Reader 的被動模式 set/get,這次我們來處理主動模式,所謂主動模式 Server 主動會拋出 inventory 訊息給 Client,首先要先設定 Reader 的主動模式,調用 command "set readmode 2",這個命令與其他的 command set 的回傳規則不一樣,雖然它同樣是回傳 "1101\r\n701\r\n1102\r\n",但它會緊接著它要回傳的訊息,這代表著我們必須要為它另外做處理,否則會收到錯誤的資料。
下圖,set readmode 2 與其他 command set 資料格式不同:
以下是依照該台 Reader 特性處理的程式碼,"set readmode 2":
public bool SetReadMode(EnumWorkMode WorkMode)
{
if (!this.IsConnected)
{
return false;
}
switch (WorkMode)
{
case EnumWorkMode.Passive:
this.SendAndReceive("set readmode 0");
var readmode = this.SendAndReceive("set readmode 0");
if (readmode == MESSAGE_SUCCESS)
{
return true;
}
break;
case EnumWorkMode.Active:
var command = "set readmode 2\r";
SocketError error = new SocketError();
var sendData = this.Encode.GetBytes(command);
this._socketCliect.Send(sendData, 0, sendData.Length, SocketFlags.None, out error);
var bufferArray = new byte[17];
var receiveCount = this._socketCliect.Receive(bufferArray);
var receive = this.Encode.GetString(bufferArray, 0, receiveCount);
return receive == MESSAGE_SUCCESS;
break;
default:
throw new ArgumentOutOfRangeException("WorkMode");
}
return false;
}
當調用 "set readmode 2" 後,緊接著就可以收到設備拋出的資料,不過這段路我並沒有走的很順,有卡到一些時間,一開始我是很單純的調用 Socket.Receive ,很不幸的這樣做會掉包(lose package),造成後續處理會有很大的問題,我面臨到幾個問題:
1.inventory mode 會有不同的資料長度。
2.當 Reader 還沒有資料時,Socket.Receive 會等待太長的時間。
3.讀取特定長度 byte 會有掉包的情況,需要判斷補包或棄包。
解決步驟:
1.用 Smart Sniff 觀察一個完整的 package 有多少資料量,經過一些時間的實作及驗証,最後得知在 "set inventory 0" 模式下,會有 44 bytes,我佔定這樣的長度是一個 package。
2.設定 time out 旗標,縮短等待時間。
3.依一個 package 的資料長度,處理補包。
下圖表示,最後 pakcage 明顯的有錯誤,所以必須要判斷補包或棄包:
程式碼如下:
public string ListeningReceive(int count)
{
if (!this.IsConnected)
{
return "No Connect!";
}
if (this._socketCliect == null && !this._socketCliect.Connected)
{
return null;
}
int timeOut = 100;
var tempTimeOut = this._socketCliect.ReceiveTimeout;
this._socketCliect.ReceiveTimeout = timeOut;
var arrayCount = 44 * count;
var bufferArray = new byte[arrayCount];
var socketError = new SocketError();
var receiveCount = 0;
Stopwatch sw = new Stopwatch();
sw.Start();
try
{
while (true)
{
this._remain = bufferArray.Length - receiveCount;
receiveCount += this._socketCliect.Receive(bufferArray, receiveCount, this._remain, SocketFlags.None, out socketError);
if (socketError != SocketError.Success)
{
if (this._socketCliect.Available == 0)
{
break;
}
else
{
continue;
}
}
if (receiveCount >= arrayCount)
{
//已經拿到固定長度的資料,則離開
break;
}
if (sw.Elapsed.Milliseconds <= timeOut)
{
continue;
}
sw.Stop();
//時間到了
int lostCount = receiveCount % 44;
this._remain = 44 - lostCount;
//資料不足44 bytes,則補足44 bytes
if (lostCount != 0)
{
receiveCount += this._socketCliect.Receive(bufferArray, receiveCount, this._remain, SocketFlags.None, out socketError);
}
break;
}
var result = this.Encode.GetString(bufferArray, 0, arrayCount);
this._remain = 0;
return result;
}
finally
{
this._socketCliect.ReceiveTimeout = tempTimeOut;
}
}
完成這段之後,接下來,不同的 inventory mode 只要變更 package 的長度就可以了 。
還是要提醒一下每一個 TCP Server 的規則都不一樣,千萬別以為都一樣而傻傻的抄 。
範例下載:
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET