Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wisp
GitHub Repository: wisp/impinj-reader-app
Path: blob/master/LLRP/TCPIPConnection.cs
179 views
/*
 ***************************************************************************
 *  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
            {
            }
        }
    }
}