我正在尝试使用 ros-sharp 项目连接并订阅 ROSbridge 上的主题。如果我按照测试客户端中的代码示例指定订阅时的消息类型,则效果很好:
rosSocket = new RosSocket(new RosBridgeClient.Protocols.WebSocketNetProtocol(uri), RosSocket.SerializerEnum.Newtonsoft_JSON);
string subscription_id = rosSocket.Subscribe<JointState>("/joint_states", SubscriptionHandler);
这是订阅方法:
public string Subscribe<T>(string topic, SubscriptionHandler<T> subscriptionHandler, int throttle_rate = 0, int queue_length = 1, int fragment_size = int.MaxValue, string compression = "none") where T : Message
{
string id;
lock (SubscriberLock)
{
id = GetUnusedCounterID(Subscribers, topic);
Subscription subscription;
Subscribers.Add(id, new Subscriber<T>(id, topic, subscriptionHandler, out subscription, throttle_rate, queue_length, fragment_size, compression));
Send(subscription);
}
return id;
}
委托类型是:
namespace RosSharp.RosBridgeClient
{
public delegate void SubscriptionHandler<T>(T t) where T : Message;
订阅处理程序如下:
private static void SubscriptionHandler(object message)
{
string msg = JsonConvert.SerializeObject((Message)message, Formatting.None);
Console.WriteLine(msg);
}
但是我希望能够通过字符串指定主题和消息类型并找到该类型并调用订阅方法。
我知道我可以通过详尽的 Switch 来做到这一点,例如:
public static string SubscribeSwitch(string type, string topic)
{
switch (type)
{
case "sensor_msgs/JointState":
return rosSocket.Subscribe<JointState>(topic, SubscriptionHandler);
break;
case "actionlib_msgs/GoalID":
return rosSocket.Subscribe<GoalID>(topic, SubscriptionHandler);
break;
}
return "";
}
但这会变得相当长且难以管理,特别是如果我为不同的机器人添加新的消息类型。因此,我花了一些时间尝试使用反射来获取调用 Subscribe 调用的类型,但在尝试传递具有非泛型委托类型的 Subscription 处理程序时,我似乎陷入了困境。有没有人做过类似的事情,或者提供一个如何做到这一点的例子 - 非常感谢。以下 exmaple 代码在 var subscribe 处失败: InvalidCastException: Unable to cast object of type 'System.RuntimeType' to type 'RosSharp.RosBridgeClient.Message'.
Type msgType = Type.GetType("RosSharp.RosBridgeClient.MessageTypes.Sensor.JointState,RosBridgeClient");
var mi = typeof(RosSocket).GetMethod("Subscribe");
Type[] aparams = new Type[1];
aparams[0] = msgType;
var fooRef = mi.MakeGenericMethod(aparams);
object[] test2 = new object[1];
test2[0] = msgType;
object[] test = new object[6];
test[0] = "/joint_states";
// get subscribe method info
var subscribe = typeof(RosSocketConsole).GetMethod(nameof(SubscriptionHandler), BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, test2);
test[1] = subscribe;
test[2] = 0;
test[3] = 1;
test[4] = int.MaxValue;
test[5] = "none";
string subscription_id = (string)fooRef.Invoke(rosSocket, test);
感谢您的帮助
因此,这解决了创建委托并传递到反射调用的问题,但是委托类型再次被硬编码 - 我希望它与 msgType 相同(但你不能使用 msgType 作为 CreateDelegate 类型,因为你得到 'msgType ' 是一个变量,但像类型一样使用):
Type msgType = Type.GetType("RosSharp.RosBridgeClient.MessageTypes.Sensor.JointState,RosBridgeClient");
var mi = typeof(RosSocket).GetMethod("Subscribe");
var di = typeof(RosSocketConsole).GetMethod(nameof(SubscriptionHandler), BindingFlags.NonPublic | BindingFlags.Static);
var del = Delegate.CreateDelegate(typeof(RosBridgeClient.SubscriptionHandler<JointState>), di);
var fooRef = mi.MakeGenericMethod(new Type[] { msgType });
string subscription_id = (string)fooRef.Invoke(rosSocket, new object[] { "/joint_states", del, 0, 1, int.MaxValue, "none" });
好吧,这似乎是解决方案:
rosSocket = new RosSocket(new RosBridgeClient.Protocols.WebSocketNetProtocol(uri), RosSocket.SerializerEnum.Newtonsoft_JSON);
Type msgType = Type.GetType("RosSharp.RosBridgeClient.MessageTypes.Sensor.JointState,RosBridgeClient");
var mi = typeof(RosSocket).GetMethod("Subscribe");
var mi2 = typeof(SubscriptionHandler<>);
var di = typeof(RosSocketConsole).GetMethod(nameof(SubscriptionHandler), BindingFlags.NonPublic | BindingFlags.Static);
Type template = typeof(SubscriptionHandler<>);
Type specific = template.MakeGenericType(msgType);
Delegate del = Delegate.CreateDelegate(specific, di);
var fooRef = mi.MakeGenericMethod(new Type[] { msgType });
string subscription_id = (string)fooRef.Invoke(rosSocket, new object[] { "/joint_states", del, 0, 1, int.MaxValue, "none" });
这提供了与创建委托并在运行时分配类型相关的解决方案: