us.ihmc.ros2.ROS2Topic Maven / Gradle / Ivy
The newest version!
package us.ihmc.ros2;
import java.util.Objects;
import static us.ihmc.ros2.ROS2TopicNameTools.messageTypeToTopicNamePart;
import static us.ihmc.ros2.ROS2TopicNameTools.processTopicNamePart;
/**
*
* A ROS 2 topic, represented as a String builder with a type. It is designed to be an immutable class with
* equals, hashCode, and toString support.
*
* The immutable methods start with "with".
* It considers a ROS 2 topic name to be 6 string parts put together:
*
* - prefix
* - robot name
* - module name
* - io qualifier
* - type name
* - suffix
*
*
* The toString() method and the getName() methods evaluate to the topic name i.e. /ihmc/controller/output/status
*
*
* It also is parameterized by type. This type is used to enforce topic type safety and
* increase the usefulness of helper methods.
*
* Before the message type is known, for example the IHMC_ROOT topic in ROS2Tools,
* the user would use the ? as ROS2Topic> to say the type has not yet been decided.
*
*
* The standard usage of this class would be:
*
* // evaluates to "/ihmc/atlas/rea/example_type/one"
* ROS2Topic topicName = new ROS2Topic().withPrefix("ihmc").withRobot("atlas").withModule("rea")
* .withType(ExampleTypeMessage.class).withTypeName().withSuffix("one");
*
* // use of immutable property
* // evaluates to "/ihmc"
* ROS2Topic ihmcPrefixed = new ROS2Topic().withPrefix("ihmc");
* // evaluates to "/ihmc/hello"
* ROS2Topic hello = ihmcPrefixed.withModule("hello");
*
*
* @param
*/
public class ROS2Topic
{
public static final String INPUT = "input";
public static final String OUTPUT = "output";
private final String prefix;
private final String robotName;
private final String moduleName;
private final String ioQualifier;
private final String typeName;
private final String suffix;
private final Class messageType;
/** Allows a QoS to be carried along with the topic to keep boilerplate to a minimum. */
private final ROS2QosProfile qos;
public ROS2Topic() // TODO make private and provide static root name method?
{
prefix = "";
robotName = "";
moduleName = "";
ioQualifier = "";
typeName = "";
suffix = "";
messageType = null;
qos = ROS2QosProfile.DEFAULT();
}
private ROS2Topic(String prefix,
String robotName,
String moduleName,
String ioQualifier,
String typeName,
String suffix,
Class messageType,
ROS2QosProfile qos)
{
this.prefix = prefix;
this.robotName = robotName;
this.moduleName = moduleName;
this.ioQualifier = ioQualifier;
this.typeName = typeName;
this.suffix = suffix;
this.messageType = messageType;
this.qos = qos;
}
private ROS2Topic copyIfNotEqual(String prefix,
String robotName,
String moduleName,
String ioQualifier,
String typeName,
String suffix,
Class messageType,
ROS2QosProfile qos)
{
if (equals(prefix, robotName, moduleName, ioQualifier, typeName, suffix, messageType, qos))
{
return this;
}
else
{
return new ROS2Topic<>(prefix, robotName, moduleName, ioQualifier, typeName, suffix, messageType, qos);
}
}
public ROS2Topic withPrefix(String prefix)
{
return copyIfNotEqual(processTopicNamePart(prefix), robotName, moduleName, ioQualifier, typeName, suffix, messageType, qos);
}
public ROS2Topic withRobot(String robotName)
{
return copyIfNotEqual(prefix, processTopicNamePart(robotName), moduleName, ioQualifier, typeName, suffix, messageType, qos);
}
// TODO: Allow multiple parts as String... that gets turned into part1/part2/part3
public ROS2Topic withModule(String moduleName)
{
return copyIfNotEqual(prefix, robotName, processTopicNamePart(moduleName), ioQualifier, typeName, suffix, messageType, qos);
}
public ROS2Topic withInput()
{
return withIOQualifier(INPUT);
}
public ROS2Topic withOutput()
{
return withIOQualifier(OUTPUT);
}
public ROS2Topic withIOQualifier(String ioQualifier)
{
return copyIfNotEqual(prefix, robotName, moduleName, processTopicNamePart(ioQualifier), typeName, suffix, messageType, qos);
}
public ROS2Topic withTypeName()
{
if (messageType == null)
{
throw new RuntimeException("This topic does not have a type yet. Cannot add type name");
}
return copyIfNotEqual(prefix, robotName, moduleName, ioQualifier, processTopicNamePart(messageTypeToTopicNamePart(messageType)), suffix, messageType, qos);
}
public ROS2Topic clearTypeName()
{
return copyIfNotEqual(prefix, robotName, moduleName, ioQualifier, "", suffix, messageType, qos);
}
public ROS2Topic withSuffix(String suffix)
{
return copyIfNotEqual(prefix, robotName, moduleName, ioQualifier, typeName, processTopicNamePart(suffix), messageType, qos);
}
public ROS2Topic withQoS(ROS2QosProfile qos)
{
return copyIfNotEqual(prefix, robotName, moduleName, ioQualifier, typeName, processTopicNamePart(suffix), messageType, qos);
}
public ROS2Topic withType(Class messageType)
{
if (messageType == null)
{
throw new RuntimeException("Cannot change the type of a topic to null");
}
else if (this.messageType == null)
{
return new ROS2Topic<>(prefix, robotName, moduleName, ioQualifier, typeName, suffix, messageType, qos);
}
else if (Objects.equals(messageType, this.messageType))
{
return (ROS2Topic) this;
}
else
{
throw new RuntimeException("Cannot change the type of a topic after it's already been set");
}
}
public ROS2Topic withTypeName(Class messageType)
{
return withType(messageType).withTypeName();
}
/**
* If this topic doesn't have a value for a field, take the value from the passed topic.
* We always take the passed in topic's QoS setting.
* @param topic to fill in the blanks with
*/
public ROS2Topic withTopic(ROS2Topic> topic)
{
String newPrefix = takeSecondIfFirstEmpty(prefix, topic.prefix);
String newRobotName = takeSecondIfFirstEmpty(robotName, topic.robotName);
String newModuleName = takeSecondIfFirstEmpty(moduleName, topic.moduleName);
String newIOQualifier = takeSecondIfFirstEmpty(ioQualifier, topic.ioQualifier);
String newTypeName = takeSecondIfFirstEmpty(typeName, topic.typeName);
String newSuffix = takeSecondIfFirstEmpty(suffix, topic.suffix);
ROS2QosProfile newQoS = topic.getQoS();
if (topic.messageType != null && !topic.messageType.equals(messageType))
{
throw new RuntimeException("Cannot change the type of a topic with the withTopic method");
}
return new ROS2Topic<>(newPrefix, newRobotName, newModuleName, newIOQualifier, newTypeName, newSuffix, messageType, newQoS);
}
private String takeSecondIfFirstEmpty(String first, String second)
{
if (first.isEmpty())
{
return second;
}
else
{
return first;
}
}
public Class getType()
{
return messageType;
}
public ROS2QosProfile getQoS()
{
return qos;
}
public String getName()
{
return toString();
}
@Override
public String toString()
{
String topicName = "";
topicName += prefix;
topicName += robotName;
topicName += moduleName;
topicName += ioQualifier;
topicName += typeName;
topicName += suffix;
return topicName;
}
@Override
public boolean equals(Object other)
{
if (this == other)
return true;
if (other == null || getClass() != other.getClass())
return false;
ROS2Topic> otherTopic = (ROS2Topic>) other;
return equals(otherTopic.prefix,
otherTopic.robotName,
otherTopic.moduleName,
otherTopic.ioQualifier,
otherTopic.typeName,
otherTopic.suffix,
otherTopic.messageType,
otherTopic.qos);
}
private boolean equals(String prefix,
String robotName,
String moduleName,
String ioQualifier,
String typeName,
String suffix,
Class> messageType,
ROS2QosProfile qos)
{
return Objects.equals(this.prefix, prefix) &&
Objects.equals(this.robotName, robotName) &&
Objects.equals(this.moduleName, moduleName) &&
Objects.equals(this.ioQualifier, ioQualifier) &&
Objects.equals(this.typeName, typeName) &&
Objects.equals(this.suffix, suffix) &&
Objects.equals(this.messageType, messageType) &&
Objects.equals(this.qos, qos);
}
@Override
public int hashCode()
{
return Objects.hash(prefix, robotName, moduleName, ioQualifier, typeName, suffix, messageType, qos);
}
}