/// <summary>
        /// Parse a MonitorMessage out of a string received on the pipe
        /// </summary>
        /// <param name="rawString">the string received on the pipe</param>
        /// <returns></returns>
        public static MonitorMessage ParseFromString(string rawString)
        {
            rawString = Encoding.Unicode.GetString(Encoding.ASCII.GetBytes(rawString.ToCharArray()));
            rawString = rawString.TrimEnd(new char[] { '\0' });

            MonitorMessage m = new MonitorMessage();
            string[] tags = rawString.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
            if (tags.Length < 3)
                throw new ArgumentException("raw string does not contain the minimum number of needed arguments");
            int opcode = -1;
            if (int.TryParse(tags[0], out opcode))
            {
                m.OpCode_Raw = opcode;
                m.OpCode = (OpCodes)opcode;
            }
            else
            {
                throw new ArgumentException("the first argument is not a valid value, not an integer value", "Operation type");
            }
            int err = -1;
            if (int.TryParse(tags[1], out err))
            {
                m.As = err;
            }
            else
            {
                throw new ArgumentException("the second argument is not a valid value, not an integer value", "As");
            }
            int errDesc = 0;
            if (int.TryParse(tags[2], out errDesc))
            {
                m.Aps = errDesc;
            }
            else
            {
                throw new ArgumentException("the third argument is not a valid value, not an integer value", "Aps");
            }
            if (tags.Length > 3)
            {
                m.Body = tags.Skip(3).ToArray();
            }
            else
            {
                m.Body = new string[] { };
            }
            return m;
        }
        static void TestClient(object stateInfo)
        {
            Console.WriteLine("entering TestClient");
            NamedPipeClientStream pipeClient1 =
                new NamedPipeClientStream(".", "openclmonitor_pipe",
            PipeDirection.InOut, PipeOptions.None);

            pipeClient1.Connect(5000);

            if (!pipeClient1.IsConnected)
            {
                Console.WriteLine("TestClient KO;Could not connect to pipe");
                return;
            }

            StreamString ss = new StreamString(pipeClient1);

            MonitorMessage createMsgOUT = new MonitorMessage(OpCodes.CREATE, 0, 0, "DEVICETEST");
            ss.WriteString(createMsgOUT.ToString());
            MonitorMessage createMsgIN = MonitorMessage.ParseFromString(ss.ReadString());
            Console.WriteLine("TestClient Received {0}", createMsgIN.ToString());
            pipeClient1.Close();

            NamedPipeClientStream pipeClient2 =
                new NamedPipeClientStream(".", createMsgIN.Body[0],
                PipeDirection.InOut, PipeOptions.None);

            pipeClient2.Connect(5000);

            if (!pipeClient2.IsConnected)
            {
                Console.WriteLine("TestClient KO;Could not connect to queue pipe");
                return;
            }

            ss = new StreamString(pipeClient2);

            MonitorMessage enumOUT = new MonitorMessage(OpCodes.ENUMERATE_COUNTERS, 0, 0, new string[] { "counterA", "counterB" });
            ss.WriteString(enumOUT.ToString());
            MonitorMessage enumIN = MonitorMessage.ParseFromString(ss.ReadString());
            Console.WriteLine("TestClient Received {0}", enumIN.ToString());
            StringBuilder sb = new StringBuilder();
            sb.Append("TestClient received enable counters: ");
            foreach (int cid in enumIN.BodyAsFloatArray)
            {
                sb.AppendFormat("{0}, ", cid);
            }
            Console.WriteLine(sb.ToString());

            {
                MonitorMessage mOut = new MonitorMessage(OpCodes.PERF_INIT, 0, 0);
                ss.WriteString(mOut.ToString());
                MonitorMessage mIn = MonitorMessage.ParseFromString(ss.ReadString());
                Console.WriteLine("TestClient Received {0}", mIn.ToString());
            }

            {
                MonitorMessage mOut = new MonitorMessage(OpCodes.RELEASE, 0, 0);
                ss.WriteString(mOut.ToString());
                MonitorMessage mIn = MonitorMessage.ParseFromString(ss.ReadString());
                Console.WriteLine("TestClient Received {0}", mIn.ToString());
            }

            {
                MonitorMessage mOut = new MonitorMessage(OpCodes.GET_COUNTERS, 0, 0, new float[]{1.1f, 2.2f});
                ss.WriteString(mOut.ToString());
                MonitorMessage mIn = MonitorMessage.ParseFromString(ss.ReadString());
                Console.WriteLine("TestClient Received {0}", mIn.ToString());
            }

            {
                MonitorMessage mOut = new MonitorMessage(OpCodes.END, 0, 0);
                ss.WriteString(mOut.ToString());
                MonitorMessage mIn = MonitorMessage.ParseFromString(ss.ReadString());
                Console.WriteLine("TestClient Received {0}", mIn.ToString());
            }

            pipeClient2.Close();
            Console.WriteLine("TestClient queue done");
        }
        private void AsyncPipeCallback(IAsyncResult Result)
        {
            try
            {
                char[] data = new char[1024];
                pipeServer.EndWaitForConnection(Result);
                StreamReader reader = new StreamReader(pipeServer);
                StreamWriter writer = new StreamWriter(pipeServer);

                reader.ReadBlock(data, 0, 1024);
                MonitorMessage messageIN = MonitorMessage.ParseFromString(new String(data));
                Console.WriteLine("server received message " + messageIN.ToString());
                if (messageIN.As == 0 && messageIN.Body.Length>0)
                {
                    // TODO: check return code is zero
                    // create pipe name
                    string newPipeName = CreatePipeName(messageIN.Body[0]);
                    Console.WriteLine("creating pipe for device " + messageIN.Body[0]);
                    NewQueueToken token = new NewQueueToken()
                        {DeviceId=messageIN.Body[0],
                            queueName=newPipeName,
                        selectCounter = selectCounters,
                        receivedValues = receivedValues};
                    // create thread
                    ThreadPool.QueueUserWorkItem(new WaitCallback(HandleQueueThread), token);
                    if (token.semaphore.WaitOne(new TimeSpan(0, 0, 5), false))
                    {
                        // send pipe name
                        MonitorMessage messageOUT = new MonitorMessage(OpCodes.OK_MESSAGE, 0, 0, newPipeName);
                        writer.Write(messageOUT.ToString());
                        writer.Flush();
                    }
                    else
                    {
                        // timeout, aborting
                        MonitorMessage messageOUT = new MonitorMessage(OpCodes.OK_MESSAGE, 1, 0);
                        writer.Write(messageOUT.ToString());
                        writer.Flush();
                        token.abort = true;
                    }
                }
                else
                {
                    // received a message with error code
                    Console.WriteLine("return code {0}, error code {1}", messageIN.As, messageIN.Aps);
                }
                pipeServer.Disconnect();
                AsyncCallback myCallback = new AsyncCallback(AsyncPipeCallback);
                pipeServer.BeginWaitForConnection(myCallback, null);
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("Oops, exiting the thread that started the BeginWaitForConnection() on this pipe has cancelled BeginWaitForConnection().");
            }
        }