public Topper(string jsonString)
        {
            Topper message = JsonUtility.FromJson <Topper>(jsonString);

            this.op    = message.op;
            this.topic = message.topic;
        }
        private void OnMessage <Tmsg>(string message) where Tmsg : RosMessage
        {
//			Debug.LogWarning("OnMessage="+message);

            if ((message != null) && !message.Equals(string.Empty))
            {
                Topper topper = new Topper(message);

                if ("publish".Equals(topper.op))                  // Topic
                {
                    RosbridgeJson <Tmsg> rosbridgeJson = null;

                    foreach (var sub in this.subscribers)
                    {
                        // only consider subscribers with a matching topic
                        if (topper.topic != sub.Key.Topic)
                        {
                            continue;
                        }

                        if (rosbridgeJson == null)
                        {
                            rosbridgeJson = new RosbridgeJson <Tmsg>(message);
                        }

                        MessageTask newTask = new MessageTask(sub.Key, rosbridgeJson.msg);

                        lock (this.lockMsgQueue)
                        {
                            this.msgQueue[rosbridgeJson.topic].Enqueue(newTask);
                        }
                    }
                }
                else if ("call_service".Equals(topper.op))                 // Service
                {
                    RosbridgeJson <Tmsg> rosbridgeJson = new RosbridgeJson <Tmsg>(message);

                    foreach (var srv in this.serviceProviders)
                    {
                        if (srv.Key.Name == rosbridgeJson.service)
                        {
                            ServiceArgs args = null;
//							ServiceResponse response = null;

                            // if we have args, parse them (args are optional on services, though)
                            Match match = Regex.Match(message, @"""args""\s*:\s*({.*}),");
                            if (match.Success)
                            {
                                args = srv.Key.ParseRequest(match.Groups[1].Value);
                            }

                            // add service request to queue, to be processed later in Render()
                            lock (this.lockMsgQueue)
                            {
                                this.svcQueue[srv.Key.Name].Enqueue(new ServiceTask(srv.Key, args, rosbridgeJson.id));
                            }

                            break;                             // there should only be one server for each service
                        }
                    }
                }
                else
                {
                    Debug.LogWarning("Unhandled message:\n" + message);
                }
            }
            else
            {
                Debug.Log("Got an empty message from the web socket");
            }
        }
        private void OnMessage(string message)
        {
            if ((message != null) && !message.Equals(string.Empty))
            {
                Topper topper = new Topper(message);

                string op = topper.op;

                if ("publish".Equals(op))                  // Topic
                {
                    string topic     = topper.topic;
                    string msgParams = string.Empty;

                    // if we have message parameters, parse them
                    Match      match = Regex.Match(message, @"""msg""\s*:\s*({.*}),");
                    RosMessage msg   = null;

                    if (match.Success)
                    {
                        msgParams = match.Groups[1].Value;
                    }

                    foreach (var sub in this.subscribers)
                    {
                        // only consider subscribers with a matching topic
                        if (topic != sub.Key.Topic)
                        {
                            continue;
                        }

                        msg = sub.Key.ParseMessage(msgParams);
                        MessageTask newTask = new MessageTask(sub.Key, msg);

                        lock (this.lockMsgQueue)
                        {
                            this.msgQueue[topic].Enqueue(newTask);
                        }
                    }
                }
                else if (Regex.IsMatch(message, @"""op""\s*:\s*""call_service"""))                 // Service
                {
                    ServiceHeader header = new ServiceHeader(message);

                    foreach (var srv in this.serviceProviders)
                    {
                        if (srv.Key.Name == header.service)
                        {
                            ServiceArgs args = null;
//							ServiceResponse response = null;

                            // if we have args, parse them (args are optional on services, though)
                            Match match = Regex.Match(message, @"""args""\s*:\s*({.*}),");
                            if (match.Success)
                            {
                                args = srv.Key.ParseRequest(match.Groups[1].Value);
                            }

                            // add service request to queue, to be processed later in Render()
                            lock (this.lockMsgQueue)
                            {
                                this.svcQueue[srv.Key.Name].Enqueue(new ServiceTask(srv.Key, args, header.id));
                            }

                            break;                             // there should only be one server for each service
                        }
                    }
                }
                else
                {
                    Debug.LogWarning("Unhandled message:\n" + message);
                }
            }
            else
            {
                Debug.Log("Got an empty message from the web socket");
            }
        }