Coder Social home page Coder Social logo

tramway's Introduction

TramwayFX

Concurrent Programming assignment.

Process-based simulation in Java/JavaFX à la MVC (more like MVP actually)

Screenshots

ASCII

tramway-ascii

JavaFX

tramway-javafx


Table of Contents

Problem

A traffic-control system to manage traffic lights of trams and cars...

Approach

As can be seen in the screenshot below, a tram (starting from the bottom left side) pass by some "obstacles":

  • The bridge
  • the intersection
  • the intersection again
  • and finally, the bridge again

Passing by each of these "obstacles" requires executing a different algorithm. The tram needs to keep record of its current section to know what algorithm to execute.

TrafficArbiter classes (BridgeArbiter and IntersectionArbiter) are used to store shared state data and execute synchronization algorithms on behalf of vehicles (Trams and Cars)


The path the tram takes can be split into four segments: 0. A-B: Starting point to bridge: Asks BridgeArbiter for permission to go EAST.

  1. B-C: Bridge to intersection: Tells IntersectionArbiter to stop cars and give only trams permission to cross.
  2. C-D: Intersection to intersection: : Tells IntersectionArbiter to stop cars and give only trams permission to cross.
  3. D-E: Intersection to bridge: Asks BridgeArbiter for permission to go WEST.

If it's in segment 0 it asks the bridge arbiter for permission to go east,

                                     [9:R]..[8:Y] (7:G)....[6:R]...  
                                                       |  |       .  
                                                       |  |       .  
   +----------------+                +-----------------+--+D-+    .  
  /                  \              /                  |  |   \ [5:Y]
 /                    \            E                   |  |   |      
 |                     ^^^^^^^^^^^^                    |  |   |      
 \                    B            \                   |  |   |      
  \                  /              \                  |  |   /      
   +A---------------+                +----------------C+--+--+       
                                                       |  |          
                                                       |  |          
         [0:G]...[1:G]                [2:Y].......[3:R]....(4:G)     

Algorithms

Bridge crossing

NOTE: Changement *may* happen when a Tram calls enter() or leave()

turn = WEST

int goingWest = 0, goingEast = 0
Semaphore canGoWest = 0, canGoEast = 0
Semaphore mutex = 1
TramGoingWest::enter() {
  p(mutex);
  goingWest++;
  if ( (turn == WEST && goingWest == 1) || (turn == EAST && goingEast == 0) ) {
    turn = WEST;
    v(canGoWest);
  }
  v(mutex);

  p(canGoWest);
}

TramGoingWest::leave() {
  p(mutex);
  goingWest--;
  if (goingWest > 0) {
    v(canGoWest);
  } else {
    if (goingEast > 0) {
      turn = EAST;
      v(canGoEast);
    }
  }
  v(mutex);
}
TramGoingEast::enter() {
  p(mutex);
  goingEast++;
  if ( (turn == EAST && goingEast == 1) || (turn == WEST && goingWest == 0) ) {
    turn = EAST;
    v(canGoEast);
  }
  v(mutex);

  p(canGoEast);
}

TramGoingEast::leave() {
  p(mutex);
  goingEast--;
  if (goingEast > 0) {
    v(canGoEast);
  } else {
    if (goingWest > 0) {
      turn = WEST;
      v(canGoWest);
    }
  }
  v(mutex);
}

Traffic intersection

NOTE: "Changement" may happen when a Tram calls enter() or leave(), not timeout-based!

int passingTrams = 0

Semaphore canGoNorth = 1, lightNorthSem = 1
Semaphore canGoSouth = 1, lightSouthSem = 1
Semaphore mutex = 1
CarGoingNorth::enter() {
  p(canGoNorth);
  p(lightNorthSem);
}

CarGoingNorth::leave() {
  v(lightNorthSem);
  v(canGoNorth);
}
CarGoingSouth::enter() {
  p(canGoSouth);
  p(lightSouthSem);
}

CarGoingSouth::leave() {
  v(lightSouthSem);
  v(canGoSouth);
}
Tram::enter() {
  p(mutex);
  passingTrams++;
  if (passingTrams == 1) {
    v(mutex);
    p(lightNorthSem);
    p(lightSouthSem);
  } else {
    v(mutex);
  }
}

Tram::leave() {
  p(mutex);
  passingTrams--;
  if (passingTrams == 0) {
    v(mutex);
    v(lightNorthSem);
    v(lightSouthSem);
  } else {
    v(mutex);
  }
}

Implementation

Classes

UML class diagrams

  • Each Tram and Car is has a thread.

  • WorldModel is responsible for creating the initial trams, and starting a timer that generates cars (heading north or south) randomly.

  • WorldController interpretes the WorldModel and updates the WorldView. This happens either at the request of the ModelView (in case of JavaFX, see below) or automatically every few millis.

See the JavaDoc comments for a description of individual classes and methods.

JavaFX Animation

  • To actually animate Trams and Cars on their paths, and to play, pause, and manually progress (jumpTo) animations: PathTransition.

  • To update relative/paused trams: AnimationTimer.

Notes

  • Tried to follow Google's Java style guide

  • Used Collections.syncronizedList(..) with ArrayList to ensure thread-safety when manipulating vehicle queues in WorldModel.

  • We call worldController.updateView() from the JavaFX's AnimationTimer because otherwise the WorldController cannot make changes to the WorldView: JavaFX's threads and objects can only be manipulated by/from another JavaFX thread.

License

Wanis Ramdani & Abdeldjalil Hebal, under CC BY 3.0

tramway's People

Contributors

wanisramdani avatar djalilhebal avatar

Stargazers

 avatar

Watchers

James Cloos avatar  avatar  avatar

tramway's Issues

Wowify WorldViewText

Alternative #5.

Upgrade the ASCII map (map.txt) to be a little bit more eye-pleasing.

  • Enrich the representation with box-drawing characters

  • String toEmojiView(String asciiView): Use 🚗️🚋️ ⬛️⬜️🌉️ 💚️❤️💛️

  • String[][] toTilesMatrix(String asciiView): Figure out a good mapping from enrichedText to assets/tiles like rpg-urban-pack

  • BufferedImage toBufferedImage(String[][] tilesMatrix): Generate from the mapped tiles matrix using BufferedImage

  • Wrap the bufferedImage as a basic GUI using the java.awt package

How to manage complexity in WorldView

// Doesn't need to be public
class Wrapper {
  public Shape shape;
  public PathTransition pathTransition;

  Wrapper(Shape shape, PathTransition pathTransition) {
    this.shape = shape;
    this.pathTransition pathTransition;
  }

}

HashMap<String, Wrapper> things = new HashMap<String, Wrapper>();

Now you can implement WorldViewInterface methods like this (same for Cars):

addTram(id):
  things.add("tram" + id, new Wrapper(new Rec, new PathTransition));

getTramProgress(id):
  return things.get("tram" + id).pathTransition.getCurrentTime();

setTramProgress(id, dur):
  things.get("tram" + id).pathTransition.setCurrentTime(dur);

setTramDynamic(id, yes):
  if (yes) things.get("tram" + id).pathTransition.play();
  else things.get("tram" + id).pathTransition.pause;

Impossible to synchronize the model and view with only logic segments

Anchor points for trams (from 'A' to 'U') and for cars ('a' to 'd' and 'e' to 'h')

                                     [9:R]..[8:Y] (7:G)....[6:R]...  
                                                       |ed|       .  
                                                       |  |       .  
   R----------------K                N-----------------MfcL--K    .  
  /                  \              /                  |  |   J [5:Y]
 S                    \P          O/                   |  |   |      
 |                     C^^^^^^^^^^D                    |  |   |      
 T                    /            \                   |  |   |      
  \U                 /              \                  |  |   I      
   A----------------B                E-----------------FgbG--H       
                                                       |  |          
                                                       |ha|          
         [0:G]...[1:G]                [2:Y].......[3:R]....(4:G)     

Vehicles should execute different algorithms depending on their graphic segment (entering/leaving a logic segment):

CAR_GOING_NORTH_SEGMENTS = {
  0: a->b (enter)
  1: c->d (nothing)
  2: c->d (leave)
}

CAR_GOING_NORTH_SEGMENTS = {
  0: e->f (enter)
  1: f->g (nothing)
  2: g->h (leave)
}

TRAM_SEGMENTS = {
  0 : A, B, C (bridge::enter)
  0': C, D (bridge::leave)
  1 : D, E, F (intersection::enter)
  1': F, G (intersection::leave)
  2 : G, H, I, J, K, L (intersection::enter)
  2': L, M (intersection::leave)
  3 : M, N, O/D (bridge::enter)
  3': O/D, P/C (bridge::leave)
  4 : P, U/A (nothing)
}

Iterating hashset returns NullPointerException

The methods that raise this exception:
playAll(), pauseAll() and resetAll()

How this error is triggered:
Assuming our HashSet has 2 trams and 1 car,
it will loop one more time searching for a third tram which won't be found

things.get("tram_" + i).pathTransition.play();

Suggested workflow for developing WorldView

  • Draw the map (paths and lights) using FXML

  • Set key points (paths) and ids (lights)

  • Write setLightColor and test it

  • Add a Tram (tram0) and ensure it makes a smooth, cyclic PathTransition animation

  • Write and test setTramDynamic(..) (using buttons maybe)

  • Write and test getTramProgress(..) (using a text for debugging maybe)

  • Write and test setTramProgress(..) (using a button maybe)

  • ...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.