public enum Color {
	GREEN, YELLOW, RED
}; public class TrackCommand extends DTO {
	public final static String TOPIC = "osgi/trains/track/command";

	public enum Type {
		SIGNAL, SWITCH
	}

	public Type type;
	public String segment;
	public Color signal;
	public Boolean alternate;
	
} public enum Type {
	SIGNAL, SWITCH
} public class Segment extends DTO {

	public enum Type {
		STRAIGHT, CURVED, SWITCH, SIGNAL, LOCATOR, BLOCK;
	}
	

	/**
	 * Our Identity
	 */
	public String id;
	
	/**
	 * Type of the segment
	 */
 	public Type type;

	/**
 	* Name of the track.This is the name required to get permission 
 	* from the Track Manager. If this name starts with X then no admission is required. This 
 	* is generally for switches.
 	*/
 	public String track;
 	
	/**
	 * Sequence of the segment in the track
	 */
	public int sequence;

	/**
	 * Length of the segment in millimeters, can be zero. It an be zero because
	 * the relation between the controllers and the switches and the signals is
	 * not always 1:1 mappable to controllers. Therefore zero sized segments can
	 * be used to map correctly.
	 */
	public int length;

	/**
	 * Short code for RFID tag attached to this segment, or 0 for none.
	 */
	public int tagCode;

	/**
	 * The associated controller
	 */
	public int controller = -1;

	/**
	 * The next segment. Only a switch can have an additional to. The second to
	 * is the one selected if the switch is set to its alternative.
	 */
	public String to[];
	
	/**
	 * The previous segment. Only a switch can have an additional from. The second from
	 * is the one selected if the switch is set to its alternative.
	 */
	public String from[];

} public interface TrackForSegment extends TrackInfo {

	/**
	 * Send when a train has been identified at a locator segment
	 * 
	 * @param rfid
	 *            The RFID for the train
	 * @param segment
	 *            The name of the segment
	 */
	void locatedTrainAt(String rfid, String segment);

	/**
	 * Indicate that a switch has been set
	 * 
	 * @param segment
	 *            the name of the switch segment
	 * @param alternative
	 *            defines the normal or alternative track
	 */
	void switched(String segment, boolean alternative);
	
	/**
	 * Indicate that a signal changed color
	 * 
	 * @param segment
	 *            the name of the signal segment
	 * @param color
	 *            the new signal color
	 */
	void signal(String segment, Color color);
	
	/**
	 * External indication that a segment is broken 
	 */
	
	void blocked(String segment, String reason, boolean blocked);
} public interface RFIDSegmentController extends SegmentController {

	/**
	 * Return the last seen RFID
	 */
	String lastRFID();
	
	/**
	 * Read an RFID. Resolves the promise when a new RFID is read.
	 * @return
	 */
	Promise<String> nextRFID();
} public interface SegmentController {

	/**
	 * Service property for identifying this controller
	 */
	String CONTROLLER_ID = "controller.id";

	/**
	 * Service property for identifying this controller, by segment name
	 */
	String CONTROLLER_SEGMENT = "controller.segment";

	/**
	 * Get the host location of the segment controller.
	 */
	default String getLocation() {
		return "unknown";
	}

} public interface SignalSegmentController extends SegmentController {

	/**
	 * Set the signal to the given color
	 * @param color
	 */
	void signal(Color color);

	Color getSignal();
} public interface SwitchSegmentController extends SegmentController {

	/**
	 * Set the switch to normal or the alternative
	 * @param alternative
	 */
	void swtch(boolean alternative);
	
	boolean getSwitch();
} public interface TrainLocator {

	/**
	 * get train location. Promise is resolved when train passes next location.
	 * @return colon separated string "trainId:segmentName"
	 */
	Promise<String> nextLocation();
}
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import osgi.enroute.trains.cloud.api.Color;
import osgi.enroute.trains.cloud.api.TrackCommand;
import osgi.enroute.trains.cloud.api.TrackCommand.Type;
import osgi.enroute.trains.cloud.api.Segment;
import osgi.enroute.trains.cloud.api.TrackForSegment;
import osgi.enroute.trains.controller.api.RFIDSegmentController;
import osgi.enroute.trains.controller.api.SegmentController;
import osgi.enroute.trains.controller.api.SignalSegmentController;
import osgi.enroute.trains.controller.api.SwitchSegmentController;
import osgi.enroute.trains.controller.api.TrainLocator;
package osgi.enroute.trains.track.controller.provider;




/**
 * The TrackController listens for Command events and performs those on the
 * right Segment Controller
 */
@Component(name = "osgi.enroute.trains.track.controller",
        property = { "event.topics=" + TrackCommand.TOPIC },
        immediate = true)
public class TrackControllerImpl implements EventHandler {
    static Logger logger = LoggerFactory.getLogger(TrackControllerImpl.class);

    private TrackForSegment trackManager;

    private Map<Integer, RFIDSegmentController> rfids = Collections
            .synchronizedMap(new HashMap<Integer, RFIDSegmentController>());
    private Map<Integer, SignalSegmentController> signals = Collections
            .synchronizedMap(new HashMap<Integer, SignalSegmentController>());
    private Map<Integer, SwitchSegmentController> switches = Collections
            .synchronizedMap(new HashMap<Integer, SwitchSegmentController>());

    @Override
    public void handleEvent(Event event) {
        Type type = (Type) event.getProperty("type");
        String s = (String) event.getProperty("segment");

        Segment segment = trackManager.getSegments().get(s);

        if (segment == null) {
            error("Segment <{}> does not exist", s);
            return;
        }

        if (!segment.type.toString().equals(type.toString())) {
            error("Event type<{}> doesn't match Segment type<{}>", type, segment.type);
            return;
        }

        switch (type) {

        case SIGNAL:
            Color color = (Color) event.getProperty("signal");