• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Scala-Robotics-Simulator / PPS-22-srs / #219

08 Aug 2025 08:23AM UTC coverage: 71.116% (-0.2%) from 71.35%
#219

Pull #32

github

sceredi
chore(deps): migrate from monix to cats.effect
Pull Request #32: chore(deps): migrate from monix to cats.effect

0 of 33 new or added lines in 6 files covered. (0.0%)

9 existing lines in 4 files now uncovered.

650 of 914 relevant lines covered (71.12%)

10.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/scala/io/github/srs/controller/ControllerModule.scala
1
package io.github.srs.controller
2

3
import scala.compiletime.deferred
4
import scala.concurrent.duration.FiniteDuration
5

6
import cats.effect.IO
7
import cats.effect.std.Queue
8
import cats.syntax.all.*
9
import io.github.srs.model.*
10
import io.github.srs.model.SimulationConfig.SimulationStatus
11
import io.github.srs.model.UpdateLogic.*
12
import io.github.srs.model.logic.*
13
import io.github.srs.utils.SimulationDefaults.SimulationConfig.maxCount
14

15
/**
16
 * Module that defines the controller logic for the Scala Robotics Simulator.
17
 */
18
object ControllerModule:
19

×
20
  /**
21
   * Controller trait that defines the interface for the controller.
22
   *
23
   * @tparam S
24
   *   the type of the state, which must extend [[ModelModule.State]].
25
   */
26
  trait Controller[S <: ModelModule.State]:
27
    /**
×
28
     * Starts the controller with the initial state.
29
     *
30
     * @param initialState
31
     *   the initial state of the simulation.
32
     */
33
    def start(initialState: S): IO[Unit]
34

35
    /**
36
     * Runs the simulation loop, processing events from the queue and updating the state.
37
     *
38
     * @param s
39
     *   the current state of the simulation.
40
     * @param queue
41
     *   a concurrent queue that holds events to be processed.
42
     * @return
43
     *   an [[IO]] task that completes when the simulation loop ends.
44
     */
45
    def simulationLoop(s: S, queue: Queue[IO, Event]): IO[Unit]
46

47
  end Controller
48

49
  /**
50
   * Provider trait that defines the interface for providing a controller.
51
   *
52
   * @tparam S
53
   *   the type of the state, which must extend [[ModelModule.State]].
54
   */
55
  trait Provider[S <: ModelModule.State]:
56
    val controller: Controller[S]
57

58
  /**
59
   * Defines the dependencies required by the controller module. In particular, it requires a
60
   * [[io.github.srs.view.ViewModule.Provider]] and a [[io.github.srs.model.ModelModule.Provider]].
61
   */
62
  type Requirements[S <: ModelModule.State] =
63
    io.github.srs.view.ViewModule.Provider[S] & io.github.srs.model.ModelModule.Provider[S]
64

65
  /**
66
   * Component trait that defines the interface for creating a controller.
67
   *
68
   * @tparam S
69
   *   the type of the simulation state, which must extend [[ModelModule.State]].
70
   */
71
  trait Component[S <: ModelModule.State]:
72
    context: Requirements[S] =>
×
73
    given inc: IncrementLogic[S] = deferred
74

75
    given tick: TickLogic[S] = deferred
76

77
    given pause: PauseLogic[S] = deferred
78

79
    given resume: ResumeLogic[S] = deferred
80

81
    given stop: StopLogic[S] = deferred
82

83
    object Controller:
84
      /**
×
85
       * Creates a controller instance.
86
       *
87
       * @return
88
       *   a [[Controller]] instance.
89
       */
90
      def apply(): Controller[S] = new ControllerImpl
91

×
92
      /**
93
       * Private controller implementation that delegates the simulation loop to the provided model and view.
94
       */
95
      private class ControllerImpl extends Controller[S]:
96

×
97
        override def start(initialState: S): IO[Unit] =
98
          val randInt: Int = initialState.simulationRNG.nextIntBetween(0, maxCount)._1
×
99
          val list = List.fill(randInt)(Event.Increment)
×
100
          for
×
NEW
101
            queueSim <- Queue.unbounded[IO, Event]
×
102
            //            queueSim <- Queue.unbounded[IO, Event]()
×
103
            _ <- context.view.init(queueSim)
104
            _ <- produceEvents(queueSim, list)
105
            _ <- simulationLoop(initialState, queueSim)
106
          yield ()
NEW
107

×
108
        override def simulationLoop(s: S, queue: Queue[IO, Event]): IO[Unit] =
109
          def loop(state: S): IO[Unit] =
×
NEW
110
            for
×
111
              events <- queue.tryTakeN(Some(50))
×
UNCOV
112
              newState <- handleEvents(events, state)
×
113
              _ <- context.view.render(newState)
114
              nextState <-
115
                if newState.simulationStatus == SimulationStatus.RUNNING then
116
                  tickEvents(newState.simulationSpeed.tickSpeed, newState)
117
                else IO.pure(newState)
118
              stop = newState.simulationStatus == SimulationStatus.STOPPED ||
119
                newState.simulationTime.exists(max => newState.elapsedTime >= max)
120
              _ <- if stop then IO.unit else loop(nextState)
121
            yield ()
122

×
123
          loop(s)
NEW
124

×
125
        private def produceEvents[A](queue: Queue[IO, A], events: List[A]): IO[Unit] =
126
          events.traverse_(queue.offer)
×
NEW
127

×
128
        private def tickEvents(tickSpeed: FiniteDuration, state: S): IO[S] =
NEW
129
          for
×
130
            _ <- IO.sleep(tickSpeed)
×
UNCOV
131
            tick <- handleEvent(Event.Tick(tickSpeed), state)
×
132
          yield tick
NEW
133

×
134
        private def handleEvents(events: Seq[Event], state: S): IO[S] =
UNCOV
135
          for finalState <- events.foldLeft(IO.pure(state)) { (taskState, event) =>
×
UNCOV
136
              for
×
137
                currentState <- taskState
138
                newState <- handleEvent(event, currentState)
139
              yield newState
140
            }
141
          yield finalState
×
NEW
142

×
143
        private def handleEvent(event: Event, state: S): IO[S] =
144
          event match
×
145
            case Event.Increment if state.simulationStatus == SimulationStatus.RUNNING =>
×
146
              context.model.increment(state)
×
147
            case Event.Tick(deltaTime) if state.simulationStatus == SimulationStatus.RUNNING =>
×
148
              context.model.tick(state, deltaTime)
×
149
            case Event.Pause => context.model.pause(state)
×
150
            case Event.Resume => context.model.resume(state)
×
151
            case Event.Stop => context.model.stop(state)
×
NEW
152
            case Event.TickSpeed(speed) => context.model.tickSpeed(state, speed)
×
153
            case _ => IO.pure(state)
×
UNCOV
154

×
155
      end ControllerImpl
156

157
    end Controller
158

159
  end Component
160

161
  /**
162
   * Interface trait that combines the provider and component traits for the controller module.
163
   *
164
   * @tparam S
165
   *   the type of the simulation state, which must extend [[ModelModule.State]].
166
   */
167
  trait Interface[S <: ModelModule.State] extends Provider[S] with Component[S]:
168
    self: Requirements[S] =>
169
end ControllerModule
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc