Unable to use substring() function within an array. (Parsing irishrail)

edited April 8 in Library Questions

I am getting an error when I attempt to use substring() as I iterate through an array. I want to extract an integer from a string of text. This works fine outside of an array:

    String trainInfo = "E215\n17:30 - Howth to Bray (-3 mins late)\nArrived Dublin Connolly next stop Tara Street";
    int start      = trainInfo.indexOf("(" ) + 1;  // STEP 1 
    int end        = trainInfo.indexOf(" mins", start);      // STEP 2
    String status  = trainInfo.substring(start, end);    // STEP 3
    int status_no   = int(status);                    // STEP 4
    println(status_no);

Whereas in my case I get an error at the point of the substring():

    void requestData() {   
      for (int i = 0; i < children.length; i++) {     

        XML announcementElement = children[i].getChild("PublicMessage");
        String announcement = announcementElement.getContent(); 

        int start      = announcement.indexOf("(" ) + 1;        // STEP 1 
        int end        = announcement.indexOf(" mins", start);  // STEP 2
        String status  = announcement.substring(start, end);    // STEP 3    ***error thrown at this point***
        int status_no   = int(status);                          // STEP 4

        println(status_no);
      }
    } 

[edit] Though not strictly related to Processing, I found a thread in the following forum that indicates that javascript does not permit substrings within arrays. I guess the same applies to Processing?: https://stackoverflow.com/questions/42429413/javascript-error-array-substring-is-not-a-function

The responder recommends to "slice and then remove the commas" or to use join(''). I have tried to to slice() and join(), based on Processing documentation but it's not working out.

Would anyone know of a way to get around this? Many thanks :)

Answers

  • I'm a bit confused -- you provide Processing(Java) example code, but then reference p5.js (JavaScript). Could you clarify which you are seeking a solution for? Or is it both?

  • Sorry, Jeremy. I meant that I just saw that answer there while googling the issue. I'm working in Processing only. I'll edit last the message now.

  • edited April 7

    Which error do you get in which line please?

    use println( start, stop); to find out if start and stop are good / correct values

    maybe one entry in the xml is empty (or has no "(" or " mins")

    use if(.......) then to skip the substring for that line

  • edited April 4

    Yes, both integers that go in to substring(), 'start' and 'end', print okay individually.

    The error occurs on line 9 of the previous code posted:

    void requestData() {   
      for (int i = 0; i < children.length; i++) {     
    
        XML announcementElement = children[i].getChild("PublicMessage");
        String announcement = announcementElement.getContent(); 
    
        int start      = announcement.indexOf("(" ) + 1;        // STEP 1 
        int end        = announcement.indexOf(" mins", start);  // STEP 2
        String status  = announcement.substring(start, end);    // STEP 3    ***error thrown at this point***
        int status_no   = int(status);                          // STEP 4
    
        println(status_no);
      }
    }
    

    The error that it throws is as so:

    Screen Shot 2018-04-04 at 06.12.22

    I'm sorry but I'm not sure what you mean by "use if then"?

  • edited April 7

    What does the error say?

    Well, when ( is missing or mins is missing start or stop would be wrong / unusable

    Or is announcement empty maybe?

  • edited April 4

    This an example of the XML string I'm using: I'm iterating through many of these.

    "E215\n17:30 - Howth to Bray (-3 mins late)\nArrived Dublin Connolly next stop Tara Street"

    The issue could lie in the fact that "('minutes' mins late)" occurs in most but not all strings I'm iterating through. An example:

    "P281\nMidleton to Cork\nExpected Departure 13:45"

    All the following seem to println okay; "announcement", "start" and "end".

    When I comment out the "substring()" line it runs okay but if I leave it uncommented I get this error:

        at processing.core.PApplet.method(PApplet.java:3730)
        at processing.core.PApplet$2.run(PApplet.java:3768)
    java.lang.StringIndexOutOfBoundsException: String index out of range: -1
        at java.lang.String.substring(String.java:1967)
        at Advice_From_Forum_MySQL_STILL_WORKS_Parsing_Status.requestData(Advice_From_Forum_MySQL_STILL_WORKS_Parsing_Status.java:58)
        at sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at processing.core.PApplet.method(PApplet.java:3730)
        at processing.core.PApplet$2.run(PApplet.java:3768)
    
  • I am trying to get around it by outputting a 0 value if "(" and " mins" don't occur within the string in which it is searching, like so:

        if ((start  == announcement.indexOf("(" ) + 1) && (end  == announcement.indexOf(" mins", start))){  
          String apples  = announcement.substring(start, end);    // STEP 3
          status   = int(apples);                    // STEP 4
        } 
        else {
          status   = 0;
        }
    

    That seems to throw a 0 every time however.

  • edited April 7

    Yes indeed, when there is no ( or mins, start and stop have value-1 or so

    Use if to check this to avoid error

  • Answer ✓

    You can add some debugging lines:

    println("announcement contains "+announcement);
    println("Current start-stop: "+ (announcement.indexOf("(" ) + 1)) + " - " +(announcement.indexOf(" mins", start)) );
    

    And then see how to handle the errors, or spot a bug if there is any.

    Kf

  • edited April 7 Answer ✓
    int start      = announcement.indexOf("(" ) + 1;        // STEP 1 
    
    int end        = announcement.indexOf(" mins", start);  // STEP 2
    
    if(start>=0&&end>=start){
    
        String status  = announcement.substring(start, end);    // STEP 3    ***error thrown at this point***
    
        int status_no   = int(status); 
    
    }
    
  • Thank you very much, Chrisir and Kfajer. With your help I got there in the end :)

      for (int i = 0; i < children.length; i++) {     
        XML announcementElement = children[i].getChild("PublicMessage");
        String announcement = announcementElement.getContent();
        int start = announcement.indexOf("(" ) + 1; // STEP 1 
        int end = announcement.indexOf(" mins", start); // STEP 2
        if(start >= 0 && end >= start){ 
          String status = announcement.substring(start, end); // STEP 3 error thrown at this point 
          onTimeStatus = int(status);
        } else {
          onTimeStatus = 666;  // My personal Error Code
        }
    
  • :-bd

    Kf

  • But now you need to do something with it

    Eg calculate the average delays of trains for weekdays or for hours during a day

    Or draw a graph in the for loop

  • Yup, already dong things. Drawn the train positions to a map to update when the XML feed updates and sending some messages over OSC to max for some complimentary sonification :)

    import de.fhpotsdam.unfolding.*;
    import de.fhpotsdam.unfolding.geo.*;
    import de.fhpotsdam.unfolding.utils.*;
    import de.fhpotsdam.unfolding.marker.*;
    
    import oscP5.*;
    import netP5.*;
    OscP5 oscP5;
    NetAddress myRemoteLocation;
    
    UnfoldingMap map;
    XML xml;   
    Location irelandLoc;
    
    Timer timer = new Timer(1000);
    
    int onTimeStatus;
    
    
    
    void setup() {
      xml = loadXML("http://" + "api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML");    // Load the XML feed from URL  
      size(800, 600, P2D);
      map = new UnfoldingMap(this);
      MapUtils.createDefaultEventDispatcher(this, map);
      irelandLoc = new Location(53.098, -7.910);   
      map.zoomAndPanTo(irelandLoc, 7);
    
      oscP5 = new OscP5(this, 236);
      myRemoteLocation = new NetAddress("127.0.0.1", 12345);   
    } 
    
    
    
    void requestData() {   
      OscMessage myMessage = new OscMessage("/onTimeStatus");  
      XML[]   children = xml.getChildren("objTrainPositions");    // Get all the child nodes named "objTrainPositions" 
      for (int i = 0; i < children.length; i++) {     
        XML announcementElement = children[i].getChild("PublicMessage");
        String announcement = announcementElement.getContent();
        int start = announcement.indexOf("(" ) + 1; // STEP 1 
        int end = announcement.indexOf(" mins", start); // STEP 2
        if(start >= 0 && end >= start){ 
          String status = announcement.substring(start, end); // STEP 3 error thrown at this point 
          onTimeStatus = int(status);
        } else {
          onTimeStatus = 666;  // My personal Error Code
        }
        myMessage.add(onTimeStatus);  
    
    
        XML trainCodeElement = children[i].getChild("TrainCode");
        String trainCode = trainCodeElement.getContent(); 
    
        XML latitudeElement = children[i].getChild("TrainLatitude");
        float latitude = latitudeElement.getFloatContent();   
    
        XML longitudeElement = children[i].getChild("TrainLongitude");
        float longitude = longitudeElement.getFloatContent();  
    
        Location location = new Location(latitude, longitude);
        map.addMarker(new SimplePointMarker(location));  // Create Unfolding Marker here
    
        println("Train Code: " + trainCode + ", Latitude: " + latitude + ", Longitude: " + longitude + ", Status: " + onTimeStatus);       
      }
      oscP5.send(myMessage, myRemoteLocation);   /* send the message */  
    } 
    
    
    void draw() {
      map.draw();
    
      if (timer.isFinished()) {   // Every one second, make a new request.
        requestData();
        timer.start();  // And restart the timer.
      }
      thread("requestData");  
    }
    
    
    class Timer {
      int savedTime;
      boolean running = false;
      int totalTime;
    
      Timer(int tempTotalTime) {
        totalTime = tempTotalTime;
      }
    
      void start() {
        running = true;
        savedTime = millis();
      }
    
      boolean isFinished() {
        int passedTime = millis() - savedTime;
        if (running && passedTime > totalTime) {
          running = false;
          return true;
        } else {
          return false;
        }
      }
    }
    
  • @Newoso Not sure if you need line 77: thread("requestData"); as you are managing the data retrieval in line 74.

    Kf

  • edited April 6

    Thanks kfrajer, but if I uninclude it, none of my train marker get drawn to screen? Also I think I followed a Schiffman example on the timer section and I'm pretty sure this is how he did it. I'll double check.

  • You need to start your timer explicitly in setup. Notice you are requesting the xml data once in setup, so I guess your intentions is to update your client? If you decide to retrieve data from the server, make sure you don't flood it with too many requests aka send request as needed.

    Kf

  • The XML feed I am accessing updates every minute. If I set my time to check every 30 secs, that should be fine shouldn't it?

    Timer timer = new Timer(30000);

    About the timer being started explicitly in setup, I'm not sure what you mean? Sorry.

    Thanks again for your input.

  • Yes, 30 secs is not bad. I mean, if you know that resources are updated every 15 minutes, they you could check about every 5 minutes. My comments was more steering toward the case you were updating every second, which could get you block by your server. This would apply in cases where you have a limited number of requests.

    About the timer being started explicitly in setup

    What I mean is to start your timer in setup. Call

    timer.start();

    This made a difference when running your code. You might not needed as maybe the initial problem was that the xml called done in setup was taking a bit longer to return. In either case, your sml loading is being done only once in setup, not every so often as you stated in your prev post, or maybe I am missing something?

    Kf

  • Sorry, I have so many scripts now I think I moved it to setup again without noticing while I was troubleshooting. D'oh!

    I think I get when you mean now. On lines 21&22 I have moved timer.start(); and requestData() to setup so that all the values load on compiling and wait to be updated from the code within draw.

    I have also commented out thread("requestData"); (line 59) as you siggested, as when I did a println I saw that it was throwing out lots of extra unwanted values.

    import de.fhpotsdam.unfolding.*;
    import de.fhpotsdam.unfolding.geo.*;
    import de.fhpotsdam.unfolding.utils.*;
    import de.fhpotsdam.unfolding.marker.*;
    UnfoldingMap map;
    SimplePointMarker trainMarker;
    
    String url = "http://" + "api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML";
    
    XML xml; 
    XML[] children;
    Location irelandLoc;
    
    Timer timer = new Timer(20000);
    
    int onTimeStatus;
    int noOfTrains;
    
    //________________________________________________________________________________________________________________________________ 
    void setup() {
      timer.start();
      requestData();
      size(800, 600, P2D);
      map = new UnfoldingMap(this);
      MapUtils.createDefaultEventDispatcher(this, map);
      irelandLoc = new Location(53.098, -7.910);   
      map.zoomAndPanTo(irelandLoc, 7);
    } 
    
    //________________________________________________________________________________________________________________________________
    void requestData() {  
      xml = loadXML(url);    // Load the XML feed from URL
      children = xml.getChildren("objTrainPositions");    // Get all the child nodes named "objTrainPositions" 
      noOfTrains = children.length;
      for (int i = 0; i < children.length; i++) {     
        XML announcementElement = children[i].getChild("PublicMessage");
        String announcement = announcementElement.getContent();
        int start = announcement.indexOf("(" ) + 1; // STEP 1 
        int end = announcement.indexOf(" mins", start); // STEP 2
        if(start >= 0 && end >= start){ 
          String status = announcement.substring(start, end); // STEP 3 error thrown at this point 
          onTimeStatus = int(status);
        } else {
          onTimeStatus = 0;  // My personal Error Code
        }
        println(onTimeStatus);
      }
    } 
    
    //________________________________________________________________________________________________________________________________ 
    void draw() {
      map.draw();
    
      if (timer.isFinished()) {   // Every one second, make a new request.
        println("Timer is finished: Requesting values and restarting");
        requestData();
        timer.start();  // And restart the timer.
      }
      //thread("requestData");   
    }
    
    //________________________________________________________________________________________________________________________________
    
    class Timer {
      int savedTime;
      boolean running = false;
      int totalTime;
    
      Timer(int tempTotalTime) {
        totalTime = tempTotalTime;
      }
    
      void start() {
        running = true;
        savedTime = millis();
      }
    
      boolean isFinished() {
        int passedTime = millis() - savedTime;
        if (running && passedTime > totalTime) {
          running = false;
          return true;
        } else {
          return false;
        }
      }
    }
    
  • edited April 6

    Oh dear, but when I re-add the code to retrieve coordinates and draw the train locations I now get a null pointer exception on line 64: map.addMarker(trainMarker);.

    But if I delete requestData(); from setup it runs fine (but the trains are obviously not drawn until the counter restarts).

    import de.fhpotsdam.unfolding.*;
    import de.fhpotsdam.unfolding.geo.*;
    import de.fhpotsdam.unfolding.utils.*;
    import de.fhpotsdam.unfolding.marker.*;
    UnfoldingMap map;
    SimplePointMarker trainMarker;
    
    String url = "http://" + "api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML";
    
    XML xml; 
    XML[] children;
    Location irelandLoc;
    
    Timer timer = new Timer(10000);
    
    int onTimeStatus;
    int noOfTrains;
    
    //________________________________________________________________________________________________________________________________ 
    void setup() {
      requestData();
      timer.start();
      size(800, 600, P2D);
      map = new UnfoldingMap(this);
      MapUtils.createDefaultEventDispatcher(this, map);
      irelandLoc = new Location(53.098, -7.910);   
      map.zoomAndPanTo(irelandLoc, 7);
    } 
    
    //________________________________________________________________________________________________________________________________
    void requestData() {  
      xml = loadXML(url);    // Load the XML feed from URL
      children = xml.getChildren("objTrainPositions");    // Get all the child nodes named "objTrainPositions" 
      noOfTrains = children.length;
      for (int i = 0; i < children.length; i++) {     
        XML announcementElement = children[i].getChild("PublicMessage");
        String announcement = announcementElement.getContent();
        int start = announcement.indexOf("(" ) + 1; // STEP 1 
        int end = announcement.indexOf(" mins", start); // STEP 2
        if(start >= 0 && end >= start){ 
          String status = announcement.substring(start, end); // STEP 3 error thrown at this point 
          onTimeStatus = int(status);
        } else {
          onTimeStatus = 0;  // My personal Error Code
        }
    
        XML trainCodeElement = children[i].getChild("TrainCode");
        String trainCode = trainCodeElement.getContent(); 
    
        XML latitudeElement = children[i].getChild("TrainLatitude");
        float latitude = latitudeElement.getFloatContent();   
    
        XML longitudeElement = children[i].getChild("TrainLongitude");
        float longitude = longitudeElement.getFloatContent();  
    
        Location location = new Location(latitude, longitude); 
    
        trainMarker = new SimplePointMarker(location);
    
        trainMarker.setStrokeWeight(3);
        trainMarker.setStrokeColor(0);
        trainMarker.setRadius(5 + onTimeStatus);
        trainMarker.setColor(color(255,0,0));
        map.addMarker(trainMarker);
    
        println(location);
        println("Train Code: " + trainCode + ", Latitude: " + latitude + ", Longitude: " + longitude + ", Status: " + onTimeStatus);  
      }
    } 
    
    //________________________________________________________________________________________________________________________________ 
    void draw() {
      map.draw();
    
      if (timer.isFinished()) {   // Every one second, make a new request.
        println("Timer is finished: Requesting values and restarting");
        requestData();
        timer.start();  // And restart the timer.
      }
    }
    
    //________________________________________________________________________________________________________________________________
    
    class Timer {
      int savedTime;
      boolean running = false;
      int totalTime;
    
      Timer(int tempTotalTime) {
        totalTime = tempTotalTime;
      }
    
      void start() {
        running = true;
        savedTime = millis();
      }
    
      boolean isFinished() {
        int passedTime = millis() - savedTime;
        if (running && passedTime > totalTime) {
          running = false;
          return true;
        } else {
          return false;
        }
      }
    }
    
  • Then just make sure your obj is not null right before interacting with it:

    if(trainMarker!=null){ ... }

    Thxs for your newer code. It is more clear and shows better what you want to do. If you use osc, don't call those function right when requesting data. Instead, create another function to handle those cases. It keeps your code more clean and each function specific to its task.

    Kf

  • Really, no need to thank me :-/ You're the one helping me out ;)

    I wasn't sure where to put if(trainMarker!=null){ ... }, so I put it in line 26 before it requests data. The train positions still don't seem to print until the timer counts down.

    Also, with the OSC as a separate function (void sendOSC(){ on line 105) and call it within the timer finished condition in draw(). It does not seem to iterate through the OSC messages.

    Basically, the overall visual end goal is to draw various vertical rectangles to the screen based on the late/early status of each train. For this I need to access the global variable 'onTimeStatus' (line 21). I cannot seem to access it in draw. I have also tried getting the requestData() (line 61) function to return an integer and retrieve it this way to no avail. Is there something basic I'm missing here?

    I have left everything visible in the code below. It runs but behaves according to the above info. Thanks again.

    import de.fhpotsdam.unfolding.*;
    import de.fhpotsdam.unfolding.geo.*;
    import de.fhpotsdam.unfolding.utils.*;
    import de.fhpotsdam.unfolding.marker.*;
    UnfoldingMap map;
    SimplePointMarker trainMarker;
    
    import oscP5.*;
    import netP5.*;
    OscP5 oscP5;
    NetAddress myRemoteLocation;
    
    String url = "http://" + "api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML";
    XML xml; 
    XML[] children;
    
    Location irelandLoc;
    
    Timer timer = new Timer(5000);
    
    int onTimeStatus;
    
    
    //________________________________________________________________________________________________________________________________ 
    void setup() {
      if(trainMarker!=null){  
        requestData();
      }
      timer.start();
      size(800, 800, P2D);
      map = new UnfoldingMap(this);
      MapUtils.createDefaultEventDispatcher(this, map);
      irelandLoc = new Location(53.098, -7.910);   
      map.zoomAndPanTo(irelandLoc, 7);
    
      oscP5 = new OscP5(this, 236);
      myRemoteLocation = new NetAddress("127.0.0.1", 12345);    
    } 
    
    
    //________________________________________________________________________________________________________________________________ 
    void draw() {
      map.draw();
    
      if (timer.isFinished()) {   // Every one second, make a new request.
        println("Timer is finished: Requesting values and restarting");
        requestData();
        sendOSC();
        timer.start();  // And restart the timer. 
      }
    
      //for (int i = 0; i < children.length; i++) {     
      //  stroke(0);
      //  fill(0,255,0);
      //  rect(0+(i*width/children.length), height/2+children.length, width/children.length, onTimeStatus);
      //}  
    }
    
    
    //________________________________________________________________________________________________________________________________
    int requestData() {   
      xml = loadXML(url);    // Load the XML feed from URL
      children = xml.getChildren("objTrainPositions");    // Get all the child nodes named "objTrainPositions" 
    
      for (int i = 0; i < children.length; i++) {   
        XML announcementElement = children[i].getChild("PublicMessage");
        String announcement = announcementElement.getContent();
        int start = announcement.indexOf("(" ) + 1; // STEP 1 
        int end = announcement.indexOf(" mins", start); // STEP 2
        if(start >= 0 && end >= start){ 
          String status = announcement.substring(start, end); // STEP 3 error thrown at this point 
          onTimeStatus = int(status);
        } else {
          onTimeStatus = 0;  // My personal Error Code
        }
    
        XML trainCodeElement = children[i].getChild("TrainCode");
        String trainCode = trainCodeElement.getContent(); 
    
        XML latitudeElement = children[i].getChild("TrainLatitude");
        float latitude = latitudeElement.getFloatContent();   
    
        XML longitudeElement = children[i].getChild("TrainLongitude");
        float longitude = longitudeElement.getFloatContent();  
    
        Location location = new Location(latitude, longitude); 
    
        trainMarker = new SimplePointMarker(location);
        trainMarker.setStrokeWeight(3);
        trainMarker.setStrokeColor(0);
        trainMarker.setRadius(5 + onTimeStatus);
        trainMarker.setColor(color(255,0,0));
    
        map.addMarker(trainMarker);    
    
        println("Train Code: " + trainCode + ", Latitude: " + latitude + ", Longitude: " + longitude + ", Status: " + onTimeStatus);  
      } 
      return onTimeStatus;  //
    } 
    
    
    //________________________________________________________________________________________________________________________________
    void sendOSC(){
      OscMessage myMessage = new OscMessage("/onTimeStatus");   
      for (int i=0; i< children.length; i++) {  
        //myMessage.add(children[i].onTimeStatus);  // Does not work
        myMessage.add(onTimeStatus);
      }
      oscP5.send(myMessage, myRemoteLocation);   /* send the message */ 
    }
    
    
    //________________________________________________________________________________________________________________________________
    
    class Timer {
      int savedTime;
      boolean running = false;
      int totalTime;
    
      Timer(int tempTotalTime) {
        totalTime = tempTotalTime;
      }
    
      void start() {
        running = true;
        savedTime = millis();
      }
    
      boolean isFinished() {
        int passedTime = millis() - savedTime;
        if (running && passedTime > totalTime) {
          running = false;
          return true;
        } else {
          return false;
        }
      }
    }
    
  • edited April 8

    This does not address all the questions. Check the code below where I have made some subtle changes all over the place.

    Also, with the OSC as a separate function (void sendOSC(){ on line 105) and call it within the timer finished condition in draw(). It does not seem to iterate through the OSC messages.

    Not clear what you mean. Where is this iteration taking place?

    One additional note. When managing your data, you need to make sure you have valid data. Just because you call a function and you are expecting certain result doesn't mean your returned value has those values. The proper way to do this is to manage your returns. If your return has data, process it. Otherwise, skip or even better, provide an error or warning msg.

    Kf

    import de.fhpotsdam.unfolding.*;
    import de.fhpotsdam.unfolding.geo.*;
    import de.fhpotsdam.unfolding.utils.*;
    import de.fhpotsdam.unfolding.marker.*;
    UnfoldingMap map;
    SimplePointMarker trainMarker;
    
    import oscP5.*;
    import netP5.*;
    
    final int ERROR_REQ=-1;
    
    OscP5 oscP5;
    NetAddress myRemoteLocation;
    
    String url = "http://" + "api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML";
    XML xml; 
    XML[] children;
    
    Location irelandLoc;
    
    Timer timer = new Timer(5000);
    int onTimeStatus;
    
    
    //________________________________________________________________________________________________________________________________ 
    void setup() {
      size(800, 800, P2D);
      map = new UnfoldingMap(this);
      MapUtils.createDefaultEventDispatcher(this, map);
      irelandLoc = new Location(53.098, -7.910);   
      map.zoomAndPanTo(irelandLoc, 7);
    
      oscP5 = new OscP5(this, 236);
      myRemoteLocation = new NetAddress("127.0.0.1", 12345);
    
      requestData();
      timer.start();
    } 
    
    
    //________________________________________________________________________________________________________________________________ 
    void draw() {
      map.draw();
    
      if (timer.isFinished()) {   // Every one second, make a new request.
        println("Timer is finished: Requesting values and restarting");
    
        if (requestData() != ERROR_REQ)
          sendOSC();
    
        timer.start();  // And restart the timer.
      }
      drawRects();
    }
    
    //________________________________________________________________________________________________________________________________ 
    void drawRects() {
    
      if (children==null)
        return;
    
      for (int i = 0; i < children.length; i++) {     
        stroke(0);
        fill(0, 255, 0);
        rect(0+(i*width/children.length), height/2+children.length, width/children.length, onTimeStatus);
      }
    }
    
    //________________________________________________________________________________________________________________________________
    int requestData() {   
      xml = loadXML(url);    // Load the XML feed from URL
      children = xml.getChildren("objTrainPositions");    // Get all the child nodes named "objTrainPositions" 
    
      if (children==null)
        return ERROR_REQ;
    
      for (int i = 0; i < children.length; i++) {   
        XML announcementElement = children[i].getChild("PublicMessage");
        String announcement = announcementElement.getContent();
        int start = announcement.indexOf("(" ) + 1; // STEP 1 
        int end = announcement.indexOf(" mins", start); // STEP 2
        if (start >= 0 && end >= start) { 
          String status = announcement.substring(start, end); // STEP 3 error thrown at this point 
          onTimeStatus = int(status);
        } else {
          onTimeStatus = 0;  // My personal Error Code
        }
    
        XML trainCodeElement = children[i].getChild("TrainCode");
        String trainCode = trainCodeElement.getContent(); 
    
        XML latitudeElement = children[i].getChild("TrainLatitude");
        float latitude = latitudeElement.getFloatContent();   
    
        XML longitudeElement = children[i].getChild("TrainLongitude");
        float longitude = longitudeElement.getFloatContent();  
    
        Location location = new Location(latitude, longitude); 
    
        trainMarker = new SimplePointMarker(location);
        trainMarker.setStrokeWeight(3);
        trainMarker.setStrokeColor(0);
        trainMarker.setRadius(5 + onTimeStatus);
        trainMarker.setColor(color(255, 0, 0));
    
        map.addMarker(trainMarker);    
    
        println("Train Code: " + trainCode + ", Latitude: " + latitude + ", Longitude: " + longitude + ", Status: " + onTimeStatus);
      } 
      return onTimeStatus;  //
    } 
    
    
    //________________________________________________________________________________________________________________________________
    void sendOSC() {
      OscMessage myMessage = new OscMessage("/onTimeStatus");   
      for (int i=0; i< children.length; i++) {  
        //myMessage.add(children[i].onTimeStatus);  // Does not work
        myMessage.add(onTimeStatus);
      }
      oscP5.send(myMessage, myRemoteLocation);   /* send the message */
    }
    
    
    //________________________________________________________________________________________________________________________________
    
    class Timer {
      int savedTime;
      boolean running;
      int totalTime;
    
      Timer(int tempTotalTime) {
        totalTime = tempTotalTime;
        running = false;  
      }
    
      void start() {
        running = true;
        savedTime = millis();
      }
    
      boolean isFinished() {  //  *** This functions needs to be revised. See post below
        if(running){
          int passedTime = millis() - savedTime;
          return running=passedTime > totalTime;
        }
        else
        return false;    
      }
    }
    
  • edited April 7

    Regarding the bad explanation of OSC messages iteration, Sorry. To simplify, what I meant by:

    Also, with the OSC as a separate function (void sendOSC(){ on line 105) and call it within the timer finished condition in draw(). It does not seem to iterate through the OSC messages.

    I will just take 'latitude' as an example..

    I rewrote some code to simplify. I don't understand why only the value of the first index of 'latitude' is being sent for all indices (line 19), when all discrete indices print out fine in the line before it. I hope this made sense.

    void requestData() {   
      xml = loadXML(url);   
      children = xml.getChildren("objTrainPositions");   
    
      for (int i = 0; i < children.length; i++) {    
        XML latitudeElement = children[i].getChild("TrainLatitude");
        latitude = latitudeElement.getFloatContent();       
        sendOSC(latitude);                                 
      } 
    } 
    
    
    void sendOSC(float messageToBeSent){
      OscMessage myMessage = new OscMessage("/latitude");  
      for (int i=0; i< children.length; i++) {  
        myMessage.add(messageToBeSent);
      }
      println(messageToBeSent);                    //*** if latitude prints for all indexes correctly at this point...
      oscP5.send(myMessage, myRemoteLocation);     //.. why does it not transmit OSC messages the same way here? ***
    }
    
  • Thank you very much for putting those hints in your previous code. I'll try to figure it out.

  • edited April 8

    Ok, so I think my code above needs to be changed. IsFinished() should be:

    boolean isFinished() {
        if (running) {
          int passedTime = millis() - savedTime;
          running=(passedTime < totalTime);
          return !running;
        } else
          return true;
      }
    

    Right now your code doesn't work as the bus system must be down. This is not going to solve your problem: You need to choose another port number. Usually lower numbers are ports reserved for the system. You should use instead numbers above 5000 or 10000 as the chances they are taken is very low.

    To test the oscP5, I use this modified version of the example provided here: http://www.sojamo.de/libraries/oscP5/examples/oscP5sendReceive/oscP5sendReceive.pde

    You will see that I have attached 10 numbers and I can retrieve them in the other end. See the documentation for proper function calls. In your case:

    1. You need to make sure you are attaching all the values. As I said, ensure you have attached those values. at least during debugging sessions.
    2. What code is reading your osc message? Is this also another Processing program?

    Kf

    /**
     * oscP5sendreceive by andreas schlegel
     * example shows how to send and receive osc messages.
     * oscP5 website at http://www.sojamo.de/oscP5
     */
    
    import oscP5.*;
    import netP5.*;
    
    
    final int N=10;
    
    OscP5 oscP5;
    NetAddress myRemoteLocation;
    
    void setup() {
      size(400, 400);
      frameRate(25);
      /** start oscP5, listening for incoming messages at port 12000 */
      oscP5 = new OscP5(this, 12000);
    
      /** myRemoteLocation is a NetAddress. a NetAddress takes 2 parameters,
       * an ip address and a port number. myRemoteLocation is used as parameter in
       * oscP5.send() when sending osc packets to another computer, device, 
       * application. usage see below. for testing purposes the listening port
       * and the port of the remote location address are the same, hence you will
       * send messages back to this sketch.
       */
      myRemoteLocation = new NetAddress("127.0.0.1", 12000);
    }
    
    
    void draw() {
      background(0);
    }
    
    
    void mousePressed() {
      /** in the following different ways of creating osc messages are shown by example */
      OscMessage myMessage = new OscMessage("/test");
    
      for (int i=0; i<N; i++)
        myMessage.add(123+i); /* add an int to the osc message */
    
    
      /** send the message */
      oscP5.send(myMessage, myRemoteLocation);
    }
    
    
    /* incoming osc message are forwarded to the oscEvent method. */
    void oscEvent(OscMessage theOscMessage) {
      /* print the address pattern and the typetag of the received OscMessage */
      print("### received an osc message.");
      print(" addrpattern: "+theOscMessage.addrPattern());
      println(" typetag: "+theOscMessage.typetag());
    
      println(theOscMessage);
      println("____________________");
      println(theOscMessage.getBytes());
    }
    
  • Thanks kfrajer :) I'm sending OSC to Max.

    I have used OSC with processing and max in the past. In case there were any crossed wires, my OSC setup was working fine, until I tried to set it up within it's own function, as you suggested.

    I think I have an idea why only the first index of 'onTimeStatus' could be accessed outside of the requestData() function. I think the index values of 'onTimeStatus' needed to be stored in an array to enable them to be used outside of that function?

    I am pretty sure this is not elegant coding, and you are probably going to vomit when you see this :-& .... but, after some hours this the only method that has allowed me to both draw those rectangles and send OSC messages using a discrete OSC function (line 95) that is called each time the timer counts down from within requestData() (line 82).

    I am using a intArray list on line 17 to store the myTrainStatus values. Even though I'm inexperienced, I can tell that this is not the best way. But it works, for now. It is messy that I am having to use myStatuses = statuses.get(i); in two places; line 55 and 98.

    import de.fhpotsdam.unfolding.*;
    import de.fhpotsdam.unfolding.geo.*;
    import de.fhpotsdam.unfolding.utils.*;
    import de.fhpotsdam.unfolding.marker.*;
    UnfoldingMap map;
    SimplePointMarker trainMarker;
    Location irelandLoc;
    
    import oscP5.*;
    import netP5.*;
    OscP5 oscP5;
    NetAddress myRemoteLocation;
    
    //String url = "http://" + "api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML";
    XML xml; 
    XML[] children;
    
    IntList statuses;
    int onTimeStatus;
    int myStatuses;
    
    Timer timer = new Timer(5000);
    
    
    //________________________________________________________________________________________________________________________________ 
    void setup() {
      size(800, 800, P2D);
      map = new UnfoldingMap(this);
      MapUtils.createDefaultEventDispatcher(this, map);
      irelandLoc = new Location(53.098, -7.910);   
      map.zoomAndPanTo(irelandLoc, 7);
    
      oscP5 = new OscP5(this, 236);
      myRemoteLocation = new NetAddress("127.0.0.1", 10000);
    
      requestData();
      timer.start();
    } 
    
    
    //________________________________________________________________________________________________________________________________ 
    void draw() {
      map.draw();
    
      if (timer.isFinished()) {   // Every one second, make a new request.
        println("Timer is finished: Requesting values and restarting");   
        requestData();
        timer.start();  // And restart the timer. 
      }
    
      for (int i = 0; i < children.length; i++) {   
        myStatuses = statuses.get(i);
        drawRects(i);
      }
    
    }
    
    
    //________________________________________________________________________________________________________________________________ 
    void requestData() {   
      xml = loadXML("xml.xml");   
      children = xml.getChildren("objTrainPositions");   
      statuses = new IntList();
    
    
      for (int i = 0; i < children.length; i++) {    
        XML announcementElement = children[i].getChild("PublicMessage");
        String announcement = announcementElement.getContent();
        int start = announcement.indexOf("(" ) + 1; // STEP 1 
        int end = announcement.indexOf(" mins", start); // STEP 2
        if(start >= 0 && end >= start){ 
          String status = announcement.substring(start, end); // STEP 3 error thrown at this point 
          onTimeStatus = int(status);
        } else {
          onTimeStatus = 999;  // My personal Error Code
        }
        statuses.append(onTimeStatus);                         
      }   
      sendOSC();
    } 
    
    
    //________________________________________________________________________________________________________________________________
    void drawRects(float i) {  
      stroke(0);
      fill(0,255,0);
      rect(0+(i*width/children.length), height/2+children.length, width/children.length, myStatuses*50);
    }
    
    
    //________________________________________________________________________________________________________________________________
    void sendOSC(){  
      OscMessage myMessage = new OscMessage("/status");  
      for (int i=0; i< children.length; i++) {
        myStatuses = statuses.get(i);
        myMessage.add(myStatuses);
        println(myStatuses);
      }
      oscP5.send(myMessage, myRemoteLocation);     
    }
    
    
    
    //________________________________________________________________________________________________________________________________
    class Timer {
      int savedTime;
      boolean running = false;
      int totalTime;
    
      Timer(int tempTotalTime) {
        totalTime = tempTotalTime;
      }
    
      void start() {
        running = true;
        savedTime = millis();
      }
    
      boolean isFinished() {
        int passedTime = millis() - savedTime;
        if (running && passedTime > totalTime) {
          running = false;
          return true;
        } else {
          return false;
        }
      }
    }
    
  • Here is the XML file I am using while that server is down (there are currently no trains running).

    <ArrayOfObjTrainPositions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://api.irishrail.ie/realtime/">
        <objTrainPositions>
            <TrainStatus>N</TrainStatus>
            <TrainLatitude>53.1442</TrainLatitude>
            <TrainLongitude>-6.06085</TrainLongitude>
            <TrainCode>E917</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>...</PublicMessage>
            <Direction>Northbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.2557</TrainLatitude>
            <TrainLongitude>-6.11317</TrainLongitude>
            <TrainCode>E214</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>...</PublicMessage>
            <Direction>Southbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.2756</TrainLatitude>
            <TrainLongitude>-6.10333</TrainLongitude>
            <TrainCode>E916</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>...</PublicMessage>
            <Direction>Northbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.2991</TrainLatitude>
            <TrainLongitude>-6.16512</TrainLongitude>
            <TrainCode>E108</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>...</PublicMessage>
            <Direction>Southbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.3206</TrainLatitude>
            <TrainLongitude>-6.21112</TrainLongitude>
            <TrainCode>E808</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E808\n17:10 - Greystones to Malahide (1 mins late)\nArrived Sydney Parade next stop Sandymount
            </PublicMessage>
            <Direction>Northbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.3531</TrainLatitude>
            <TrainLongitude>-6.24591</TrainLongitude>
            <TrainCode>E215</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E215\n17:30 - Howth to Bray (-3 mins late)\nArrived Dublin Connolly next stop Tara Street
            </PublicMessage>
            <Direction>Southbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.3629</TrainLatitude>
            <TrainLongitude>-6.22753</TrainLongitude>
            <TrainCode>E915</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E915\n17:00 - Bray to Howth (3 mins late)\nDeparted Clontarf Road next stop Killester
            </PublicMessage>
            <Direction>Northbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.392</TrainLatitude>
            <TrainLongitude>-6.11448</TrainLongitude>
            <TrainCode>E216</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E216\n17:45 - Howth to Bray (1 mins late)\nDeparted Sutton next stop Bayside
            </PublicMessage>
            <Direction>Southbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>T</TrainStatus>
            <TrainLatitude>53.1442</TrainLatitude>
            <TrainLongitude>-6.06085</TrainLongitude>
            <TrainCode>E107</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E107\n16:27 - Howth to Greystones(2 mins late)\nTERMINATED Greystones at 17:47
            </PublicMessage>
            <Direction>Southbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>T</TrainStatus>
            <TrainLatitude>53.4509</TrainLatitude>
            <TrainLongitude>-6.15649</TrainLongitude>
            <TrainCode>E807</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E807\n16:43 - Bray to Malahide(0 mins late)\nTERMINATED Malahide at 17:50
            </PublicMessage>
            <Direction>Northbound</Direction>
        </objTrainPositions>
    </ArrayOfObjTrainPositions>
    
  • It is messy that I am having to use myStatuses = statuses.get(i); in two places

    Not, it is not messy. You have some functions that need to access data. They access their data and their own convenient time. Can this be done more efficient? Maybe. However for this case you don't really need to go for optimization when you can get a more clear code. Nowadays computers are fast enough that nobody will notice in your case.

    Glad to hear you figure your problem. All the best,

    Kf

  • Thanks for your feedback, kfajer. I surely would not have gotten to this point without your tips and guidance. Much obliged :) Have a nice day.

Sign In or Register to comment.