/*
***************************************************************************
* Copyright 2008 Impinj, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************
*/
/*
***************************************************************************
* File Name: TCPIPConnection.cs
*
* Author: Impinj
* Organization: Impinj
* Date: 18 Jan, 2008
*
* Description: This file contains implementation of TCPIP communication
* classes including client and server
***************************************************************************
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Runtime.Remoting;
using System.Collections;
using System.Xml;
using System.Xml.Serialization;
using System.Data;
using LLRP.DataType;
namespace LLRP
{
/// <summary>
/// TCPIPClient, used for building LLRPClient
/// </summary>
class TCPIPClient : CommunicationInterface
{
private const int BUFFER_SIZE = 1024;
private TcpClient tcp_client;
private NetworkStream ns;
private bool new_message = true;
private Int16 msg_ver;
private Int16 msg_type;
private Int32 msg_len = 0;
private Int32 msg_id;
private byte[] msg_data;
private Int32 msg_cursor = 0;
private bool trying_to_close = false;
private object syn_msg = new object();
/// <summary>
/// Message received event.
/// </summary>
public TCPIPClient()
{
state = new AsynReadState(BUFFER_SIZE);
}
/// <summary>
/// Open network connection
/// </summary>
/// <param name="device_name">Device name or IP address</param>
/// <param name="port">TCP port</param>
/// <returns>true if opened succefully, otherwise false</returns>
public override bool Open(string device_name, int port)
{
try
{
//Estabilish connection, get network stream for reading and writing
tcp_client = new TcpClient(device_name, port);
ns = tcp_client.GetStream();
if (ns == null) return false;
trying_to_close = false;
}
catch
{
return false;
}
ns.Flush();
//Start asyn-read
ns.BeginRead(state.data, 0, BUFFER_SIZE, new AsyncCallback(OnDataRead), state);
return true;
}
/// <summary>
/// Asyn read result process
/// </summary>
/// <param name="ar"></param>
private void OnDataRead(IAsyncResult ar)
{
int offset = 0; //used to keep the start position of a LLRP message in
//byte array returned from the read
AsynReadState ss = (AsynReadState)ar.AsyncState; //used to keep data
lock (syn_msg)
{
try
{
REPEAT:
if (new_message) //new_message is a flag to indicate if the data is part of unfinished message
{
msg_cursor = 0;
int reserved_date_len = ss.data.Length - offset;
if (reserved_date_len > 10)
{
//Calculate message type, version, length and id
int header = (ss.data[offset] << 8) + ss.data[offset + 1];
try
{
msg_type = (Int16)(header & 0x03FF);
msg_ver = (Int16)((header >> 10) & 0x07);
msg_len = (ss.data[offset + 2] << 24) + (ss.data[offset + 3] << 16) + (ss.data[offset + 4] << 8) + ss.data[offset + 5];
msg_id = (ss.data[offset + 6] << 24) + (ss.data[offset + 7] << 16) + (ss.data[offset + 8] << 8) + ss.data[offset + 9];
}
catch
{
msg_len = 0;
}
if (msg_len > 20000)
{
int i = 0;
}
//if data length larger than needed data for a complete message,
//copy data into existing message and triggered message event
if (msg_len > 0 && msg_ver == 1)
{
msg_data = new byte[msg_len];
//if message length greater than the calcualted message length. copy message and trigger message event
if (ss.data.Length >= (offset + msg_len))
{
Array.Copy(ss.data, offset, msg_data, 0, msg_len);
delegateMessageReceived msgRecv = new delegateMessageReceived(TriggerMessageEvent);
msgRecv.BeginInvoke(msg_ver, msg_type, msg_id, msg_data, null, null);
offset += msg_len;
new_message = true;
goto REPEAT;
}
else//If the received data is shorter than the message length, keep reading for the next data
{
new_message = false;
Array.Copy(ss.data, offset, msg_data, 0, ss.data.Length - offset);
msg_cursor = ss.data.Length - offset;
}
}
}
else
{
new_message = true;
//if ns !=null, do next asyn-read, to ensure that read
if (ns != null && ns.CanRead)
{
try
{
ns.Flush();
state = new AsynReadState(BUFFER_SIZE);
Array.Copy(ss.data, offset, state.data, 0, reserved_date_len);
if (!trying_to_close) ns.BeginRead(state.data, reserved_date_len, BUFFER_SIZE-reserved_date_len, new AsyncCallback(OnDataRead), state);
}
catch { }
}
return;
}
}
else
{
//if data length larger than needed data for a complete message,
//copy data into existing message and triggered message event
if (ss.data.Length >= msg_len - msg_cursor)
{
Array.Copy(ss.data, 0, msg_data, msg_cursor, msg_len - msg_cursor);
delegateMessageReceived msgRecv = new delegateMessageReceived(TriggerMessageEvent);
msgRecv.BeginInvoke(msg_ver, msg_type, msg_id, msg_data, null, null);
offset += msg_len - msg_cursor;
new_message = true;
goto REPEAT;
}
else //keep reading
{
new_message = false;
Array.Copy(ss.data, 0, msg_data, msg_cursor, ss.data.Length);
msg_cursor += ss.data.Length;
}
}
//if ns !=null, do next asyn-read, to ensure that read
if (ns != null && ns.CanRead)
{
try
{
ns.Flush();
state = new AsynReadState(BUFFER_SIZE);
if (!trying_to_close) ns.BeginRead(state.data, 0, BUFFER_SIZE, new AsyncCallback(OnDataRead), state);
}
catch { }
}
}
catch
{
}
}
}
/// <summary>
/// Release network resource
/// </summary>
public override void Close()
{
trying_to_close = true;
if (ns!=null)ns.Close();
if (tcp_client != null)tcp_client.Close();
}
/// <summary>
/// Send data to device
/// </summary>
/// <param name="data">data to be sent. byte array</param>
/// <returns></returns>
public override int Send(byte[] data)
{
if (ns == null) return 0;
try
{
ns.Flush();
lock (syn_msg)
{
new_message = true;
}
ns.BeginWrite(data, 0, data.Length, null, null);
return data.Length;
}
catch
{
return -1;
}
}
/// <summary>
/// Received data
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
public override int Receive(out byte[] buffer)
{
try
{
ns.ReadTimeout = 200;
byte[] buf = new byte[8096];
int readSize = ns.Read(buf, 0, 8096);
buffer = new byte[readSize];
Array.Copy(buf, 0, buffer, 0, readSize);
return readSize;
}
catch
{
buffer = null;
return -1;
}
}
public override void Dispose()
{
this.Close();
}
}
/// <summary>
/// TCPIPServer. Used for building LLRPServer, for example: LLRP reader simulator
/// </summary>
class TCPIPServer : CommunicationInterface
{
private const Int32 BUFFER_SIZE = 1024;
private TcpListener server;
private NetworkStream ns;
private bool new_message = true;
private Int16 msg_ver;
private Int16 msg_type;
private Int32 msg_len = 0;
private Int32 msg_id;
private byte[] msg_data;
private Int32 msg_cursor = 0;
public TCPIPServer()
{
state = new AsynReadState(BUFFER_SIZE);
}
//Asyn-call back
private void DoAcceptTCPClientCallBack(IAsyncResult ar)
{
try
{
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
ns = client.GetStream();
delegateClientConnected clientConn = new delegateClientConnected(TriggerOnClientConnect);
clientConn.BeginInvoke(null, null);
ns.BeginRead(state.data, 0, BUFFER_SIZE, new AsyncCallback(OnDataRead), state);
}
catch
{
}
}
/// <summary>
/// Close connection
/// </summary>
public override void Close()
{
if(ns!=null)ns.Close();
if (server != null) server.Stop();
}
/// <summary>
/// Open socket to accept connection
/// </summary>
/// <param name="device_name">ignored</param>
/// <param name="port">TCP port</param>
/// <returns></returns>
public override bool Open(string device_name, int port)
{
try
{
IPAddress ipAddr = new IPAddress(new byte[] { 127, 0, 0, 1 });
server = new TcpListener(ipAddr, port);
server.Start();
server.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTCPClientCallBack), server);
}
catch
{
return false;
}
return true;
}
/// <summary>
/// Receive data
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
public override int Receive(out byte[] buffer)
{
try
{
ns.ReadTimeout = 200;
byte[] buf = new byte[8096];
int readSize = ns.Read(buf, 0, 8096);
buffer = new byte[readSize];
Array.Copy(buf, 0, buffer, 0, readSize);
return readSize;
}
catch
{
buffer = null;
return -1;
}
}
/// <summary>
/// Send data
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public override int Send(byte[] data)
{
try
{
ns.BeginWrite(data, 0, data.Length, null, null);
return data.Length;
}
catch
{
return -1;
}
}
/// <summary>
/// Data process
/// </summary>
/// <param name="ar"></param>
private void OnDataRead(IAsyncResult ar)
{
int offset = 0;
AsynReadState ss = (AsynReadState)ar.AsyncState;
//Detect client disconnection. then start anther acception
if (ss.data[0] == 0)
{
server.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTCPClientCallBack), server);
return;
}
try
{
REPEAT: //if multiple messages exist in the result. repeat the process
if (new_message) //new_message is a flag to indicate if the data is part of unfinished message
{
//Calculate message type, version, length and id
int header = (ss.data[offset] << 8) + ss.data[offset + 1];
msg_type = (Int16)(header & 0x03FF);
msg_ver = (Int16)((header >> 10) & 0x07);
msg_len = (ss.data[offset + 2] << 24) + (ss.data[offset + 3] << 16) + (ss.data[offset + 4] << 8) + ss.data[offset + 5];
msg_id = (ss.data[offset + 6] << 24) + (ss.data[offset + 7] << 16) + (ss.data[offset + 8] << 8) + ss.data[offset + 9];
//if the message length is 0. the rest of byte array are empty. restart non-block reading
if (msg_len == 0)
{
if (ns != null)
{
try
{
ns.Flush();
state = new AsynReadState(BUFFER_SIZE);
ns.BeginRead(state.data, 0, state.data.Length, new AsyncCallback(OnDataRead), state);
}
catch { }
return;
}
else
return;
}
msg_data = new byte[msg_len];
//if message length greater than the calcualted message length. copy message and trigger message event
if (ss.data.Length >= msg_len)
{
Array.Copy(ss.data, offset, msg_data, 0, msg_len);
delegateMessageReceived msgRecv = new delegateMessageReceived(TriggerMessageEvent);
msgRecv.BeginInvoke(msg_ver, msg_type, msg_id, msg_data, null, null);
offset += msg_len;
msg_len = 0;
goto REPEAT;
}
else//If the received data is shorter than the message length, keep reading for the next data
{
new_message = false;
Array.Copy(ss.data, 0, msg_data, 0, ss.data.Length);
msg_cursor += ss.data.Length;
}
}
else
{
//if data length larger than needed data for a complete message,
//copy data into existing message and triggered message event
if (ss.data.Length >= msg_len - msg_cursor)
{
Array.Copy(ss.data, 0, msg_data, msg_cursor, msg_len - msg_cursor);
delegateMessageReceived msgRecv = new delegateMessageReceived(TriggerMessageEvent);
msgRecv.BeginInvoke(msg_ver, msg_type, msg_id, msg_data, null, null);
offset += msg_len - msg_cursor;
msg_len = 0;
msg_cursor = 0;
new_message = true;
goto REPEAT;
}
else //keep reading
{
new_message = false;
Array.Copy(ss.data, 0, msg_data, msg_cursor, ss.data.Length);
msg_cursor += ss.data.Length;
}
}
//if ns !=null, do next asyn-read, to ensure that read
if (ns != null && ns.CanRead)
{
try
{
ns.Flush();
state = new AsynReadState(BUFFER_SIZE);
ns.BeginRead(state.data, 0, state.data.Length, new AsyncCallback(OnDataRead), state);
}
catch { }
}
}
catch
{
}
}
}
}