Best practice of structuring a code

edited March 2017 in Arduino

So i am making this GUI for controlling a physical object through serial commands to an arduino. I wan't to know the best practice for structuring a code in processing. I usually do something like this on an arduino:

if (something) {
  InitializeGUI();
  while (cursorNotPressedOutsideBoxOrExitButtonNotPressed) {
    TUI();
    EVENTS();
    etc();
  }
}

Then i repeat this codes in the main loop (arduinos equivalent to processings Draw loop?), for different events and this makes me able to controll the arduinos limited calculating power.

In processing i I would like to have "pop-up" windows, for different control aspects of the machine. However in processing i can see that a while-loop will stop the mouseX, mousePressed etc. from working, which makes the code stay in the while loop indefinetly.

I could make up alot of if-statements to structure my code, but is this the best-practice?

Answers

  • Answer ✓

    About the while loop: practice in Processing is to use draw() as loop.

    I am not too sure about the other part of the question. But it looks like you want to manage states, which is often explained in this forum.
    Basically, you have a variable holding a number (for example), which is the state the program is in. You change this number to change the state (classically, from start screen to game to game over screen, for example). And in draw(), you can use the switch instruction to do treatment according to the current state.

  • Answer ✓

    If I understand you correctly then you want to read data from the arduino as it becomes available, then use the collected data in a sketch that is running at 60fps.

    One way to do it is use a separate thread to read the arduino data and store data so it in a buffer, then read the data from the buffer in the main thread. I wrote the code below in answer to another forum discussion and I have modified it to simulate reading data from an arduino.

    The main problem with threads is concurrent access exceptions, this code uses a boolean flag (bufferAvailable) to prevent both threads using the same buffer (linked list) at the same time.

    BTW the code uses G4P to create the button.

    The code might give you some ideas for your project.

    import g4p_controls.*;
    import java.util.*;
    import java.awt.*;
    
    LinkedList<Point> buffer = new LinkedList<Point>();
    LinkedList<Point> path = new LinkedList<Point>();
    
    GButton btnStop;
    boolean bufferAvailable = true;
    boolean arduinoDataAvailable = true;
    
    public void setup() {
      size(800, 500, JAVA2D);
      if (frame != null) frame.setTitle("Arduino simulator");
      // Create stop button
      btnStop = new GButton(this, 20, 20, 160, 30);
      btnStop.setText("STOP READING ARDUINO");
      btnStop.addEventHandler(this, "stopArduino");
      registerMethod("pre", this);
      thread("getDataFromArduino");
    }
    
    // Simulate some data coming from Arduino in a seperate thread
    void getDataFromArduino() {
      while (arduinoDataAvailable) {
        if (bufferAvailable) {
          bufferAvailable = false;
          int x = int(random(width));
          int y = int(random(height));
          buffer.addLast(new Point(x, y));
          bufferAvailable = true;
          // simulate data arriving at ~ 40 items per second
          // i.e. 25 ms interval between data items.
          delay(25);
          // this will give the main thread a chance to process
          // data on the buffer.
        }
      }
    }
    
    // This method will be responsible for removing data from
    // the buffer when it is available
    void pre() {
      if (bufferAvailable && !buffer.isEmpty()) {
        bufferAvailable = false;
        path.addLast(buffer.removeFirst());
        bufferAvailable = true;
      }
    }
    
    public void draw() {
      background(0, 0, 128); 
      stroke(220, 220, 0);
      for (int i = 1; i < path.size (); i++) {
        Point s = path.get(i-1);
        Point e = path.get(i);
        line(s.x, s.y, e.x, e.y);
      }
      // Only interested in the last 200 data elements from arduino
      while(path.size() > 200)
        path.removeFirst();
    }
    
    // Stop read data from arduino - not reversable in this sketch
    public void stopArduino(GButton source, GEvent event) {
      arduinoDataAvailable = !arduinoDataAvailable;
      btnStop.setVisible(false);
    }
    
    // Create all the GUI controls.
    // autogenerated do not edit
    public void createGUI() {
      frame.setTitle("Arduino simulator");
      btnStop = new GButton(this, 35, 60, 160, 30);
      btnStop.setText("STOP READING ARDUINO");
      btnStop.addEventHandler(this, "stopArduino");
    }
    
  • edited August 2014 Answer ✓

    The LinkedList object referenced by field buffer is being structurally modified by 2 threads at the same time!
    While the Thread running getDataFromArduino() method is enqueuing Point objects into it,
    Processing's "Animation" Thread is dequeuing from it within pre() callback method!

    Either make those actions synchronized or use some thread-safe structure such as ConcurrentLinkedQueue:
    http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html

    Or at least, wrap up a LinkedList or an ArrayDeque using Collections.synchronizedList():
    http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedList-java.util.List-

  • edited August 2014 Answer ✓

    @GoToLoop

    The LinkedList object referenced by field buffer is being structurally modified by 2 threads at the same time!

    I am using a boolean semaphore (bufferAvailable) to avoid concurrent access to the linked list.

    I have tested the code and despite my best efforts I couldn't get a concurrent access exception thrown :)

    Did you?

    EDIT You might look at Binary Semaphores in this

  • edited August 2014 Answer ✓

    That PDF is about C code from a teacher called Julie, whom btW I've seen some C++ YouTube CS classes! :D
    Nevertheless, I already know some Java semaphore techniques. I've even used AtomicBoolean once!
    http://forum.processing.org/two/discussion/6004/how-to-make-a-movie-array

    // Display by Last Modified (v4.53)
    
    import java.util.concurrent.atomic.AtomicBoolean;
    static final AtomicBoolean isBusy = new AtomicBoolean();
    
    protected boolean sortByModified(final File[] files) {
    
      // ...
    
      if (!isBusy.compareAndSet(false, true)) {
        println("Function sortByModified() is busy!");
        return false;
      }
    
      // ...
    
      isBusy.set(false);
      return true;
    }
    

    I concur that the way your example is set, it's very improbable for anything to go wrong! There are 2 reasons:

    1. The extra !buffer.isEmpty() condition.
    2. Consumption/dequeue @ ~60 FPS is faster than production/enqueue @ ~40 FPS.

    However, let's imagine they had about similar speeds; or even worse, production was the fastest,
    !buffer.isEmpty() condition would be irrelevant!

    Now how good bufferAvailable "semaphore" would be against all concurrency modifications in that situation?

    I daresay there'd be a slightly chance for both threads enter in their respective if blocks at the same time!
    Even before either bufferAvailable = false; would have a chance to change the semaphore status!

    Declaring bufferAvailable as volatile would make that occurrence harder, but not impossible!
    At minimum, you'd have to go w/ AtomicBoolean + compareAndSet() as a non-blocking flag like I did! <):)

  • Answer ✓

    There are 2 problems with my code

    1) It does not handle the situation where the production rate exceeds the consumption rate (same as framerate). The code was adapted from another example where the production rate was very low. Anyway thanks to GoToLoop for pointing that out.

    2) The ONLY way that we can get concurrent modification is if both if-blocks are entered at the same time. Although the probability of this happening is incredibly small that is no reason for taking the chance. Again thanks to GoToLoop for pointing that out.

    My objectives for this code were

    a) avoid use of specialised Java concurrency classes, thereby keeping it simple to understand

    b) avoid blocking the main event thread and thereby reducing the frame rate.

    c) a framework that might be adapted to suit the needs of palmhoej

    I believe this code solves the problems pointed out by GoToLoop and meets my objectives

    import g4p_controls.*;
    import java.util.*;
    import java.awt.*;
    
    LinkedList<Point> buffer = new LinkedList<Point>();
    LinkedList<Point> path = new LinkedList<Point>();
    
    GButton btnStop;
    boolean arduinoDataAvailable = true;
    // The boolean semaphore
    boolean acquired;
    
    /*
    Parameter aquire 
      pass true if you wnat to aquire a lock
      pass false if you want to release a lock
    
    The method returns true ONLY if a lock was aquired 
    otherwise it returns false.
    
    If a lock is aquired it MUST eventually be released
    otherwise the application will freeze up.
    */
    public synchronized boolean lock(boolean acquire) {
      if (!acquire) {
        acquired = false;
        return false;
      }
      // So we want to get a lock
      if (acquired) {
        return false;
      } else {
        acquired = true;
        return true;
      }
    }
    
    public void setup() {
      size(800, 500, JAVA2D);
      if (frame != null) frame.setTitle("Arduino simulator");
      // Create stop button
      btnStop = new GButton(this, 20, 20, 160, 30);
      btnStop.setText("STOP READING ARDUINO");
      btnStop.addEventHandler(this, "stopArduino");
      registerMethod("pre", this);
      thread("getDataFromArduino");
    }
    
    // Simulate some data coming from Arduino in a seperate thread
    void getDataFromArduino() {
      while (arduinoDataAvailable) {
        // Wait until buffer is available
        while (!lock (true)) {
          delay(100);
        }
        int x = int(random(width));
        int y = int(random(height));
        buffer.addLast(new Point(x, y));
        lock(false);
        // Simulate getting 200 data elements per second
        delay(5);
      }
    }
    
    // This method will be responsible for emptying all the
    // data from the buffer when it is available
    void pre() {
      if (lock(true)) {
        while (!buffer.isEmpty ()) {
          path.addLast(buffer.removeFirst());
        }
        lock(false);
      }
      println(buffer.size());
      // Keep the last 200 data elements produced
      while (path.size () > 200)
        path.removeFirst();  
    }
    
    public void draw() {
      background(0, 0, 128); 
      stroke(220, 220, 0);
      for (int i = 1; i < path.size (); i++) {
        Point s = path.get(i-1);
        Point e = path.get(i);
        line(s.x, s.y, e.x, e.y);
      }
    }
    
    // Stop read data from arduino - not reversable in this sketch
    public void stopArduino(GButton source, GEvent event) {
      arduinoDataAvailable = !arduinoDataAvailable;
      btnStop.setVisible(false);
    }
    
    // Create all the GUI controls.
    // autogenerated do not edit
    public void createGUI() {
      frame.setTitle("Arduino simulator");
      btnStop = new GButton(this, 35, 60, 160, 30);
      btnStop.setText("STOP READING ARDUINO");
      btnStop.addEventHandler(this, "stopArduino");
    }
    
Sign In or Register to comment.