Added new model.

This commit is contained in:
Andrew Lalis 2022-05-05 22:21:43 +02:00
parent e8cb22276a
commit 2fbc22af0d
32 changed files with 324 additions and 442 deletions

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.0</version>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>nl.andrewl</groupId>

View File

@ -1,18 +0,0 @@
package nl.andrewl.railsignalapi.dao;
import nl.andrewl.railsignalapi.model.Branch;
import nl.andrewl.railsignalapi.model.RailSystem;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface BranchRepository extends JpaRepository<Branch, Long> {
Optional<Branch> findByIdAndRailSystem(long id, RailSystem railSystem);
Optional<Branch> findByIdAndRailSystemId(long id, long railSystemId);
Optional<Branch> findByNameAndRailSystem(String name, RailSystem railSystem);
List<Branch> findAllByRailSystemOrderByName(RailSystem railSystem);
List<Branch> findAllByNameAndRailSystem(String name, RailSystem railSystem);
}

View File

@ -0,0 +1,11 @@
package nl.andrewl.railsignalapi.dao;
import nl.andrewl.railsignalapi.model.RailSystem;
import nl.andrewl.railsignalapi.model.component.Component;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ComponentRepository extends JpaRepository<Component, Long> {
void deleteAllByRailSystem(RailSystem rs);
}

View File

@ -0,0 +1,11 @@
package nl.andrewl.railsignalapi.dao;
import nl.andrewl.railsignalapi.model.RailSystem;
import nl.andrewl.railsignalapi.model.Segment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SegmentRepository extends JpaRepository<Segment, Long> {
void deleteAllByRailSystem(RailSystem rs);
}

View File

@ -1,9 +0,0 @@
package nl.andrewl.railsignalapi.dao;
import nl.andrewl.railsignalapi.model.SignalBranchConnection;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SignalBranchConnectionRepository extends JpaRepository<SignalBranchConnection, Long> {
}

View File

@ -1,26 +0,0 @@
package nl.andrewl.railsignalapi.dao;
import nl.andrewl.railsignalapi.model.Branch;
import nl.andrewl.railsignalapi.model.RailSystem;
import nl.andrewl.railsignalapi.model.Signal;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface SignalRepository extends JpaRepository<Signal, Long> {
Optional<Signal> findByIdAndRailSystem(long id, RailSystem railSystem);
Optional<Signal> findByIdAndRailSystemId(long id, long railSystemId);
boolean existsByNameAndRailSystem(String name, RailSystem railSystem);
@Query("SELECT DISTINCT s FROM Signal s " +
"LEFT JOIN s.branchConnections bc " +
"WHERE bc.branch = :branch " +
"ORDER BY s.name")
List<Signal> findAllConnectedToBranch(Branch branch);
List<Signal> findAllByRailSystemOrderByName(RailSystem railSystem);
}

View File

@ -1,9 +0,0 @@
package nl.andrewl.railsignalapi.dao;
import nl.andrewl.railsignalapi.model.Train;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface TrainRepository extends JpaRepository<Train, Long> {
}

View File

@ -1,38 +0,0 @@
package nl.andrewl.railsignalapi.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@NoArgsConstructor
@Getter
public class Branch {
@Id
@GeneratedValue
private Long id;
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private RailSystem railSystem;
@Column(nullable = false)
private String name;
@Enumerated(EnumType.STRING)
@Setter
private BranchStatus status;
@OneToMany(mappedBy = "branch", orphanRemoval = true)
private Set<SignalBranchConnection> signalConnections;
public Branch(RailSystem railSystem, String name, BranchStatus status) {
this.railSystem = railSystem;
this.name = name;
this.status = status;
this.signalConnections = new HashSet<>();
}
}

View File

@ -1,6 +0,0 @@
package nl.andrewl.railsignalapi.model;
public enum BranchStatus {
FREE,
OCCUPIED
}

View File

@ -1,5 +1,8 @@
package nl.andrewl.railsignalapi.model;
/**
* A cardinal direction, useful for some components.
*/
public enum Direction {
NORTH,
SOUTH,

View File

@ -8,6 +8,9 @@ import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* Represents a closed system that contains a collection of components.
*/
@Entity
@Getter
@NoArgsConstructor
@ -16,6 +19,9 @@ public class RailSystem {
@GeneratedValue
private Long id;
/**
* The name of this system.
*/
@Column(nullable = false, unique = true)
private String name;

View File

@ -0,0 +1,50 @@
package nl.andrewl.railsignalapi.model;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import nl.andrewl.railsignalapi.model.component.SegmentBoundaryNode;
import nl.andrewl.railsignalapi.model.component.Signal;
import javax.persistence.*;
import java.util.Set;
/**
* Represents a traversable segment of a rail system that components can
* connect to.
*/
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Segment {
@Id
@GeneratedValue
private Long id;
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private RailSystem railSystem;
/**
* A unique name for this segment.
*/
@Column(unique = true)
private String name;
/**
* The signals that are connected to this branch.
*/
@OneToMany(mappedBy = "segment")
private Set<Signal> signals;
/**
* The set of nodes from which trains can enter and exit this segment.
*/
@ManyToMany(mappedBy = "segments", cascade = CascadeType.ALL)
private Set<SegmentBoundaryNode> boundaryNodes;
@Override
public boolean equals(Object o) {
if (o == this) return true;
return o instanceof Segment s && this.id != null && this.id.equals(s.id);
}
}

View File

@ -1,40 +0,0 @@
package nl.andrewl.railsignalapi.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import java.util.Set;
@Entity
@Getter
@NoArgsConstructor
public class Signal {
@Id
@GeneratedValue
private Long id;
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private RailSystem railSystem;
@Column(nullable = false)
private String name;
@OneToMany(mappedBy = "signal", orphanRemoval = true, cascade = CascadeType.ALL)
private Set<SignalBranchConnection> branchConnections;
@Embedded
private Position position;
@Column(nullable = false)
@Setter
private boolean online = false;
public Signal(RailSystem railSystem, String name, Position position, Set<SignalBranchConnection> branchConnections) {
this.railSystem = railSystem;
this.name = name;
this.position = position;
this.branchConnections = branchConnections;
}
}

View File

@ -1,57 +0,0 @@
package nl.andrewl.railsignalapi.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Entity
@NoArgsConstructor
@Getter
public class SignalBranchConnection implements Comparable<SignalBranchConnection> {
@Id
@GeneratedValue
private Long id;
@ManyToOne(optional = false)
private Signal signal;
@ManyToOne(optional = false, cascade = CascadeType.PERSIST)
private Branch branch;
@Enumerated(EnumType.STRING)
private Direction direction;
@ManyToMany
private Set<SignalBranchConnection> reachableSignalConnections;
public SignalBranchConnection(Signal signal, Branch branch, Direction direction) {
this.signal = signal;
this.branch = branch;
this.direction = direction;
reachableSignalConnections = new HashSet<>();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
return this.id != null &&
o instanceof SignalBranchConnection sbc && sbc.getId() != null &&
this.id.equals(sbc.getId());
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
@Override
public int compareTo(SignalBranchConnection o) {
int c = Long.compare(this.getSignal().getId(), o.getSignal().getId());
if (c != 0) return c;
return this.direction.compareTo(o.getDirection());
}
}

View File

@ -1,30 +0,0 @@
package nl.andrewl.railsignalapi.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity
@Getter
@NoArgsConstructor
public class Switch {
@Id
@GeneratedValue
private Long id;
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private RailSystem railSystem;
@Column(nullable = false)
private String name;
@Embedded
private Position position;
@Column(nullable = false)
private int state;
@Column(nullable = false)
private int maxStates;
}

View File

@ -1,20 +0,0 @@
package nl.andrewl.railsignalapi.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity
@NoArgsConstructor
@Getter
public class Train {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
private Branch currentBranch;
}

View File

@ -0,0 +1,62 @@
package nl.andrewl.railsignalapi.model.component;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import nl.andrewl.railsignalapi.model.RailSystem;
import javax.persistence.*;
/**
* Represents a physical component of the rail system that the API can interact
* with, and send or receive data from. For example, a signal, switch, or
* detector.
*/
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public abstract class Component {
@Id
@GeneratedValue
private Long id;
/**
* The rail system that this component belongs to.
*/
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private RailSystem railSystem;
/**
* The position of this component in the system.
*/
@Embedded
private Position position;
/**
* A human-readable name for the component.
*/
@Column(unique = true)
private String name;
/**
* Whether this component is online, meaning that an in-world device is
* currently connected to relay information regarding this component.
*/
@Column(nullable = false)
@Setter
private boolean online = false;
public Component(RailSystem railSystem, Position position, String name) {
this.railSystem = railSystem;
this.position = position;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
return o instanceof Component c && this.id != null && this.id.equals(c.id);
}
}

View File

@ -0,0 +1,33 @@
package nl.andrewl.railsignalapi.model.component;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import nl.andrewl.railsignalapi.model.RailSystem;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToMany;
import java.util.Set;
/**
* A node that, together with other nodes, forms a path that trains can follow
* to traverse through segments in the rail system.
*/
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class PathNode extends Component {
/**
* The set of nodes that this one is connected to.
*/
@ManyToMany
private Set<PathNode> connectedNodes;
public PathNode(RailSystem railSystem, Position position, String name, Set<PathNode> connectedNodes) {
super(railSystem, position, name);
this.connectedNodes = connectedNodes;
}
}

View File

@ -1,4 +1,4 @@
package nl.andrewl.railsignalapi.model;
package nl.andrewl.railsignalapi.model.component;
import lombok.AllArgsConstructor;
import lombok.Data;
@ -6,6 +6,9 @@ import lombok.NoArgsConstructor;
import javax.persistence.Embeddable;
/**
* A three-dimensional position for a component within a system.
*/
@Embeddable
@Data
@AllArgsConstructor

View File

@ -0,0 +1,32 @@
package nl.andrewl.railsignalapi.model.component;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import nl.andrewl.railsignalapi.model.RailSystem;
import nl.andrewl.railsignalapi.model.Segment;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import java.util.Set;
/**
* Component that relays information about trains traversing from one segment
* to another. It links exactly two segments together at a specific point.
*/
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class SegmentBoundaryNode extends PathNode {
/**
* The set of segments that this boundary node connects. This should
* generally always have exactly two segments.
*/
@ManyToMany
private Set<Segment> segments;
public SegmentBoundaryNode(RailSystem railSystem, Position position, String name, Set<PathNode> connectedNodes, Set<Segment> segments) {
super(railSystem, position, name, connectedNodes);
this.segments = segments;
}
}

View File

@ -0,0 +1,43 @@
package nl.andrewl.railsignalapi.model.component;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import nl.andrewl.railsignalapi.model.Direction;
import nl.andrewl.railsignalapi.model.RailSystem;
import nl.andrewl.railsignalapi.model.Segment;
import javax.persistence.*;
/**
* A signal is a component that relays the status of a connected segment to
* some sort of in-world representation, whether that be a certain light
* color, or electrical signal.
*/
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Signal extends Component {
/**
* The direction this signal is facing. This is the direction in which the
* signal connects to a branch.
* <pre>
* |-segment A-|-segment B-|
* ===================== <- Rail
* ]+ ---> Signal is facing East, and shows status on
* its western side. It is connected to segment B.
* </pre>
*/
@Enumerated(EnumType.STRING)
private Direction direction;
/**
* The segment that this signal connects to.
*/
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private Segment segment;
public Signal(RailSystem railSystem, Position position, String name, Segment segment, Direction direction) {
super(railSystem, position, name);
this.segment = segment;
this.direction = direction;
}
}

View File

@ -0,0 +1,29 @@
package nl.andrewl.railsignalapi.model.component;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.util.Set;
/**
* A switch is a component that directs traffic between several connected
* segments.
*/
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Switch extends PathNode {
/**
* The set of all possible configurations that this switch can be in.
*/
@OneToMany(mappedBy = "switchComponent", orphanRemoval = true, cascade = CascadeType.ALL)
private Set<SwitchConfiguration> possibleConfigurations;
/**
* The switch configuration that this switch is currently in.
*/
@OneToOne(optional = false, fetch = FetchType.LAZY)
private SwitchConfiguration activeConfiguration;
}

View File

@ -0,0 +1,31 @@
package nl.andrewl.railsignalapi.model.component;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.util.Set;
/**
* A possible connection that can be made between path nodes, if this is set
* as an active configuration in the linked switch component.
*/
@Entity
@NoArgsConstructor
public class SwitchConfiguration {
@Id
@GeneratedValue
private Long id;
/**
* The switch component that this configuration belongs to.
*/
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private Switch switchComponent;
/**
* The set of nodes that this switch configuration connects. This should
* be almost always a set of two nodes.
*/
@ManyToMany(fetch = FetchType.EAGER)
private Set<PathNode> nodes;
}

View File

@ -1,38 +0,0 @@
package nl.andrewl.railsignalapi.rest;
import lombok.RequiredArgsConstructor;
import nl.andrewl.railsignalapi.rest.dto.BranchResponse;
import nl.andrewl.railsignalapi.rest.dto.SignalResponse;
import nl.andrewl.railsignalapi.service.BranchService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping(path = "/api/railSystems/{rsId}/branches")
@RequiredArgsConstructor
public class BranchesController {
private final BranchService branchService;
@GetMapping
public List<BranchResponse> getAllBranches(@PathVariable long rsId) {
return branchService.getAllBranches(rsId);
}
@GetMapping(path = "/{branchId}")
public BranchResponse getBranch(@PathVariable long rsId, @PathVariable long branchId) {
return branchService.getBranch(rsId, branchId);
}
@GetMapping(path = "/{branchId}/signals")
public List<SignalResponse> getBranchSignals(@PathVariable long rsId, @PathVariable long branchId) {
return branchService.getConnectedSignals(rsId, branchId);
}
@DeleteMapping(path = "/{branchId}")
public ResponseEntity<?> deleteBranch(@PathVariable long rsId, @PathVariable long branchId) {
branchService.deleteBranch(rsId, branchId);
return ResponseEntity.noContent().build();
}
}

View File

@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping(path = "/api/railSystems")
@RequestMapping(path = "/api/rs")
@RequiredArgsConstructor
public class RailSystemsApiController {
private final RailSystemService railSystemService;

View File

@ -1,44 +0,0 @@
package nl.andrewl.railsignalapi.rest;
import lombok.RequiredArgsConstructor;
import nl.andrewl.railsignalapi.rest.dto.SignalConnectionsUpdatePayload;
import nl.andrewl.railsignalapi.rest.dto.SignalCreationPayload;
import nl.andrewl.railsignalapi.rest.dto.SignalResponse;
import nl.andrewl.railsignalapi.service.SignalService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping(path = "/api/railSystems/{rsId}/signals")
@RequiredArgsConstructor
public class SignalsApiController {
private final SignalService signalService;
@PostMapping
public SignalResponse createSignal(@PathVariable long rsId, @RequestBody SignalCreationPayload payload) {
return signalService.createSignal(rsId, payload);
}
@GetMapping
public List<SignalResponse> getSignals(@PathVariable long rsId) {
return signalService.getAllSignals(rsId);
}
@GetMapping(path = "/{sigId}")
public SignalResponse getSignal(@PathVariable long rsId, @PathVariable long sigId) {
return signalService.getSignal(rsId, sigId);
}
@PostMapping(path = "/{sigId}/signalConnections")
public SignalResponse updateSignalConnections(@PathVariable long rsId, @PathVariable long sigId, @RequestBody SignalConnectionsUpdatePayload payload) {
return signalService.updateSignalBranchConnections(rsId, sigId, payload);
}
@DeleteMapping(path = "/{sigId}")
public ResponseEntity<?> deleteSignal(@PathVariable long rsId, @PathVariable long sigId) {
signalService.deleteSignal(rsId, sigId);
return ResponseEntity.noContent().build();
}
}

View File

@ -1,13 +0,0 @@
package nl.andrewl.railsignalapi.rest.dto;
import nl.andrewl.railsignalapi.model.Branch;
public record BranchResponse(
long id,
String name,
String status
) {
public BranchResponse(Branch branch) {
this(branch.getId(), branch.getName(), branch.getStatus().name());
}
}

View File

@ -1,9 +0,0 @@
package nl.andrewl.railsignalapi.rest.dto;
import java.util.List;
public record SignalConnectionsUpdatePayload(
List<ConnectionData> connections
) {
public static record ConnectionData(long from, long to) {}
}

View File

@ -1,13 +0,0 @@
package nl.andrewl.railsignalapi.rest.dto;
import nl.andrewl.railsignalapi.model.Position;
import java.util.List;
public record SignalCreationPayload(
String name,
Position position,
List<BranchData> branchConnections
) {
public static record BranchData(String direction, String name, Long id) {}
}

View File

@ -1,61 +0,0 @@
package nl.andrewl.railsignalapi.rest.dto;
import nl.andrewl.railsignalapi.model.Position;
import nl.andrewl.railsignalapi.model.Signal;
import nl.andrewl.railsignalapi.model.SignalBranchConnection;
import java.util.Comparator;
import java.util.List;
public record SignalResponse(
long id,
String name,
Position position,
List<ConnectionData> branchConnections,
boolean online
) {
public SignalResponse(Signal signal) {
this(
signal.getId(),
signal.getName(),
signal.getPosition(),
signal.getBranchConnections().stream()
.sorted(Comparator.comparing(SignalBranchConnection::getDirection))
.map(ConnectionData::new)
.toList(),
signal.isOnline()
);
}
public static record ConnectionData(
long id,
String direction,
BranchResponse branch,
List<ReachableConnectionData> reachableSignalConnections
) {
public ConnectionData(SignalBranchConnection c) {
this(
c.getId(),
c.getDirection().name(),
new BranchResponse(c.getBranch()),
c.getReachableSignalConnections().stream()
.map(cc -> new ReachableConnectionData(
cc.getId(),
cc.getDirection().name(),
cc.getSignal().getId(),
cc.getSignal().getName(),
cc.getSignal().getPosition()
))
.toList()
);
}
public static record ReachableConnectionData(
long connectionId,
String direction,
long signalId,
String signalName,
Position signalPosition
) {}
}
}

View File

@ -1,9 +1,9 @@
package nl.andrewl.railsignalapi.service;
import lombok.RequiredArgsConstructor;
import nl.andrewl.railsignalapi.dao.BranchRepository;
import nl.andrewl.railsignalapi.dao.ComponentRepository;
import nl.andrewl.railsignalapi.dao.RailSystemRepository;
import nl.andrewl.railsignalapi.dao.SignalRepository;
import nl.andrewl.railsignalapi.dao.SegmentRepository;
import nl.andrewl.railsignalapi.model.RailSystem;
import nl.andrewl.railsignalapi.rest.dto.RailSystemCreationPayload;
import nl.andrewl.railsignalapi.rest.dto.RailSystemResponse;
@ -19,8 +19,8 @@ import java.util.List;
@RequiredArgsConstructor
public class RailSystemService {
private final RailSystemRepository railSystemRepository;
private final SignalRepository signalRepository;
private final BranchRepository branchRepository;
private final SegmentRepository segmentRepository;
private final ComponentRepository componentRepository;
@Transactional
public List<RailSystemResponse> getRailSystems() {
@ -40,10 +40,8 @@ public class RailSystemService {
public void delete(long rsId) {
var rs = railSystemRepository.findById(rsId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
var signals = signalRepository.findAllByRailSystemOrderByName(rs);
signalRepository.deleteAll(signals);
var branches = branchRepository.findAllByRailSystemOrderByName(rs);
branchRepository.deleteAll(branches);
componentRepository.deleteAllByRailSystem(rs);
segmentRepository.deleteAllByRailSystem(rs);
railSystemRepository.delete(rs);
}

View File

@ -9,6 +9,7 @@ import nl.andrewl.railsignalapi.dao.RailSystemRepository;
import nl.andrewl.railsignalapi.dao.SignalBranchConnectionRepository;
import nl.andrewl.railsignalapi.dao.SignalRepository;
import nl.andrewl.railsignalapi.model.*;
import nl.andrewl.railsignalapi.model.component.SignalBranchConnection;
import nl.andrewl.railsignalapi.rest.dto.SignalConnectionsUpdatePayload;
import nl.andrewl.railsignalapi.rest.dto.SignalCreationPayload;
import nl.andrewl.railsignalapi.rest.dto.SignalResponse;