Final task ISS-2021 Bologna: Automated Car-Parking

Problem analysis
Sprint 1
Sprint 2

Project

Sprint 3

Six parking slots

The car park now has six parking slots available as requested by the customer. At each entry or exit of a car from the car park, a variable is updated with the current status of each slot, which can be vacant or full. At first we assume that the system has all six parking spaces empty and then, after each request, the trolley carries each car into a slot that will be colored red in the ManagerGUI.

Each parking slot is represented by a triple that indicates the position on the map and the direction that the trolley must take when it is in front of that specific parking slot. These coordinates are found in locationsKb.pl and correspond to the following:

parking1(1, 1, e).
parking2(1, 2, e).
parking3(1, 3, e).
parking4(4, 1, w).
parking5(4, 2, w).
parking6(4, 3, w).

The finite-state machine describing the business logic remains essentially the same. The only difference concerning its states is that the state findSlot has been removed and now the automaton moves directly from acceptOUT to moveToSlotOut. That's because checking the correctness of a TOKENID and finding its SLOTNUM are now the same thing since we used a map to store such pairs.

Alarms and automatic fan

The outdoorsentinelactor and the temperaturesentinelactor have been implemented faithfully to their previous description and to the alarms diagram. The only difference with their implementation in the first executable model is that they now also update their CoAP resource with the last issued alarm (or revocation of an alarm) to inform the external GUI and the test plan.

When the user sets the fan to be handled automatically, parkservicestatusguiactor remains responsible to turn on and off the fan. It does it by itself, listening for temperature alarms and sending messages to fanactor according to them.

Trolley start and stop

The trolleyactor required only minimal editing by introducing a new state called stopped in which it waits for a startTrolley message to resume its behavior. New requests received while stopped are queued and processed when the behavior is resumed. Moreover, the trolleyactor now specify its status by updating its CoAP resource and setting it to idle when it's in home or going home, to working when it's going elsewhere and to stopped when it's being stopped by the manager.

The external ManagerGUI reads the status of the trolley from the trolleyactor via CoAP and sends start and stop messages through the parkservicestatusguiactor, much like it already worked for the control of the fan.

The real sonar device

In addition to the use of a mock-object for the simulation of the presence/absence of the car in the OUTDOOR, a real sensor device (HCSR04) was used to perform the function of the outsonar. To do this we used a piece of software obtained by the customer called SonarAlone.c, thanks to which the distance in centimeters of the object from the sensor is put on the standard output. The file was simply modified so that it did not report the values in cascade, but only the measurement at the moment in which it is requested.

Experiments shown that the minimum distance that the sonar is able to intercept is 3 cm, while the maximum is 42 cm. For this reason we established a threshold value of 25 cm, below which the car is to be considered present. The SonarDriver, adaptable to be a presence sensor, has been completed accordingly.

The complete Manager GUI and Client GUI

As a result of the enhancements previously described, the ManagerGUI is now able to show the full status of the parkin-area updated in near real-time. The functionalities of the ClientGUI are also complete. We also implemented a new component called CarparkingSenderObserver to make the code of the GUIs more readable.

Testing

To simplify the writing of test cases, we introduced a new class called CarparkingCoapObserver able to read a resource both in a non-blocking fashion and in a blocking fashion waiting for the value of the resource to change.

Tests from Sprint 1 and Sprint 2 must still hold, however they had to be adapted to deal with the multiple parking-slots. We report hereunder only the most significant fragments of each new test case:

@Test
fun checkSlots() {

  var cco = CarparkingCoapObserver("parkserviceguiactor", blocking = true, verbose = false)

  actor!!.forward("enterRequest", "enterRequest(0)", "parkmanagerserviceactor")
  var slotnum6 = cco.observePayload()
  actor!!.forward("carEnter", "carEnter(0)", "parkmanagerserviceactor")
  var tokenid6 = cco.observePayload()
  assertLocationInTime("6", "0", "N", 10000)
  assertLocationInTime("4", "3", "W", 10000)
  // REPEAT FOR EVERY PARKING-SLOT

  // ALL PARKING-SLOTS ARE NOW FULL
  actor!!.forward("enterRequest", "enterRequest(0)", "parkmanagerserviceactor")
  assertSlotnum(cco, "0")
  actor!!.forward("exitRequest", "exitRequest($tokenid4)", "parkmanagerserviceactor")
  assertLocationInTime("4", "1", "W", 10000)
  assertLocationInTime("6", "4", "S", 10000)
  actor!!.forward("exitRequest", "exitRequest($tokenid4)", "parkmanagerserviceactor")
  assertNotice(cco, "tokenid(invalid)")

}
@Test
fun checkAlarms() {

  val ccoo = CarparkingCoapObserver("outdoorsentinelactor", blocking = true)
  actor!!.emit("outdoorCleared", "outdoorCleared(0)")
  assertNoEventInTime(ccoo, 1000)
  actor!!.emit("outdoorOccupied", "outdoorOccupied(0)")
  assertNoEventInTime(ccoo, 4000)
  assertEvent(ccoo, "outdoorAlarm(0)")
  actor!!.emit("outdoorCleared", "outdoorCleared(0)")
  assertEvent(ccoo, "outdoorAlarmRevoked(0)")

  val ccof = CarparkingCoapObserver("fanactor", blocking = true)
  actor!!.forward("fanAuto", "fanAuto(auto)", "parkservicestatusguiactor")
  actor!!.emit("temperatureAlarm", "temperatureAlarm(0)")
  assertEvent(ccof, "fanStart(0)")
  actor!!.emit("temperatureAlarmRevoked", "temperatureAlarmRevoked(0)")
  assertEvent(ccof, "fanStop(0)")
  actor!!.forward("fanAuto", "fanAuto(manual)", "parkservicestatusguiactor")
  actor!!.emit("temperatureAlarm", "temperatureAlarm(0)")
  assertNoEventInTime(ccof, 1000)
  actor!!.emit("temperatureAlarmRevoked", "temperatureAlarmRevoked(0)")
  assertNoEventInTime(ccof, 1000)

}
@Test
fun checkTrolleyStop() {

  actor!!.forward("stopTrolley", "stopTrolley(0)", "trolleyactor")
  actor!!.forward("goto", "goto(parking6)", "trolleyactor")
  assertNotMovingInTime(2000)
  actor!!.forward("startTrolley", "startTrolley(0)", "trolleyactor")
  assertLocationInTime("4", "3", "W", 10000)
  assertNotMovingInTime(2000)

  actor!!.forward("goto", "goto(home)", "trolleyactor")
  delay(2000)
  actor!!.forward("stopTrolley", "stopTrolley(0)", "trolleyactor")
  assertNotMovingInTime(2000)
  actor!!.forward("startTrolley", "startTrolley(0)", "trolleyactor")
  assertLocationInTime("0", "0", "S", 10000)
  assertNotMovingInTime(2000)

}

Summary final architecture of the system

Executable model: trolley.qak Test plan: Sprint3Test.kt

Distribution of work

Fantazzini Giacomo carried out the realizzation of the mock objects (fan, thermometer, sonar, weight sensor), the dynamics of the trolley and the JavaScript code, while Badalamenti Claudia took care of the realization of the GUIs, the business logic of the project and the support for the real sonar device.
In both cases, the colleagues helped each other in the realization of each part.

By: Giacomo Fantazzini and Claudia Badalamenti
Email: giacomo.fantazzini2@studio.unibo.it - claudia.badalamenti@studio.unibo.it