-
Notifications
You must be signed in to change notification settings - Fork 8
On the communication component of the API
Use find the protobuf definition of the Network messages that are used by the system. You should generate the Java / C++ / Python / etc. classes from those definitions and connect to the network via MQTT.
-
You should extend from the AbstractCommunicationComponent.
-
In order to create a MessagingService use the MessagingServiceFactory.
-
In the AbstractCommunicationComponent you should find a field called locator, which has a TrackCommunicationServiceLocator. You should use its fields, if you would like to:
-
send commands to the network (set segment’s enabledness, set turnout’s direction, set train’s speed)
-
get status information about various elements (segment’s occupancy, segment’s enabledness, turnout’s direction)
-
register your listener for a particular message
-
-
You can find example classes, in the sample project and the demo project, which explain how you can send commands and retrieve status information.
Fields of the TrackCommunicationServiceLocator:
@Accessors(PUBLIC_GETTER, PRIVATE_SETTER) val ITrackElementStateSender trackElementStateSender
@Accessors(PUBLIC_GETTER, PRIVATE_SETTER) val ITrackElementCommander trackElementCommander
@Accessors(PUBLIC_GETTER, PRIVATE_SETTER) val ITrainCommander trainCommander
@Accessors(PUBLIC_GETTER, PRIVATE_SETTER) val IDccCommander dccCommander
@Accessors(PUBLIC_GETTER, PRIVATE_SETTER) val ITrackElementCommandCallback trackElementCommandCallback
@Accessors(PUBLIC_GETTER, PRIVATE_SETTER) val ITrackElementStateRegistry trackElementStateRegistry
@Accessors(PUBLIC_GETTER, PRIVATE_SETTER) val ITrainSpeedStateRegistry trainSpeedStateRegistry
@Accessors(PUBLIC_GETTER, PRIVATE_SETTER) val ISendAllStatusCommandCallback sendAllStatusCallback
@Accessors(PUBLIC_GETTER, PRIVATE_SETTER) val IComputerVisionCallback computerVisionCallback
What is the difference between: IMessageHandler<T>, I*[Command/State]Listener interfaces, *Client, *Callback, *StateRegistry, *Commander classes in the communication component?
This interface has declares a handleMessage(T message)
method that should handle the arrival of a message. Usually it disassembles the field of a message
This interface declares a on*[Command/State](..)
method which has several arguments, depending upon what message’s arrival should trigger this method to be invoked. The arguments should be the disassembled fields of the message.
This interface declares a on*StateChanged(..)
method which has several arguments, depending upon what status change should trigger it. E.g. if a segment’s occupancy has been changed, then the segmentId, the old occupancy (occupied/free) and the new occupancy will be the arguments of this method.
This class usually implements the IMessageHandler<T>
interface, and has a *Callback field to which it forwards the disassembled message.
This class has a constructor which expects an AbstractMessageDispatcher, and it implicitly creates a *Client and registers it as a MessageHandler in the dispatcher. So that if a particuar message arrives, this callback can be invoked.
What is more, it implements the I*CommandListener interface, so it can register listeners which will be invoked upon a particular message arrives. The user can register its message handler, if it should be notified about a particular message’s arrival.
This class is only implemented in case of a message which reports status info (see Network messages page for more info).
In its constructor it expects an AbstractMessageDispatcher, and it implicitly creates a *Callback and registers listeners inside the callback so that whenever a particular status message arrives, they will be invoked. In the listeners it usually stores each status message if it is different from the previous one. E.g. if a section’s status has changed, then it updates its status in a data structure.
What’s more, it offers a I*ChangeListener interface to be invoked, so whenever a status changes, the listener can be notified about it.
Moreover, each status can be queried by a getter method, based on the message for which this *StateRegistry was instantiated.
This class is implemented in order to send particular commands and it requires a MessagingService. It usually implements those interfaces which correspond to what commands we want to send. The arguments of these interfaces should have the fields of the message that should be sent by the commander. The commander creates the message itself and forwards it to the MessagingService.
If we want to receive DccOperationsStateMessage message, we should add a new IMessageHandler<DccOperationsStateMessage>
field to the AbstractMessageDispatcher. Then, we should implement the DccStateClient
class that disassembles the fields of the state message and invokes the onDccOperationsState
method of the DccStateCallback
field. After that we should implement the DccStateCallback
which can register the message handler in the dispatcher and invoke our listener whenever a disassembled message arrives. Finally, we can implement the ˙DccStateRegistry` in order to track the changes of the status and send change notifications if we need them.
You can find the implementation of these classes in the hu.bme.mit.inf.modes3.messaging.communication.state.dcc package of the hu.bme.mit.inf.modes3.messaging.communication project.
The interface declares two methods for conversions between the serialized and the deserialized representations of a message. By serialized representation we mean the representation that is forwarded on the network, and by deserialized repsentation we mean the representation that is used in the particular component.
-
There is an abstract dispatcher which only defines what messages it will handle, but it is not directly responsible for the translation. Instead, it can delegate this task to other classes and only get the necessarry representations from them. E.g. the ProtobufMessageDispatcher has two fields. One for the internal to protobuf conversion (InternalToProtobufConverter) and one for the other way around (ProtobufToInternalConverter). In this way the dispatcher is only responsible for invoking the particular message handlers and the conversion is done by dedicated classes. What’s more the *Client classes will be responsible only for disassembling the internal message.
Should you create a new message format, you only have to implement the internal message to your message format conversion (and the opposite direction also). See ProtobufMessageDispatcher class, how it is done.
You should define the .proto file, generate Java classes from that, create a class that extends the InternalMessage, attach a new method to the InternalToProtobufConverter and to the ProtobufToInternalConverter classes. Implement an IMessageDispatcher<YourMessageWithTheInternalMessageType>
, implement the *Client, *Callback and either the *Sender or the *StateRegistry classes, depending on if you want to send this message or if it is a status message if you want to track the status.
