How can I update a chart inside a thread?

Hi!

This is a simple question. I need to update a chart in a thread.

public class ChartThread extends Thread{

	private ListSeries ADC0;
	private ListSeries ADC1;
	private ListSeries ADC2;
	private ListSeries ADC3;
	private ListSeries ADC4;
	private ListSeries ADC5;
	
	@SuppressWarnings("unchecked")
	public ChartThread(LineChart lineChart) {
	
		
		// Get the series from the chart too
		ADC0 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(0);
		ADC1 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(1);
		ADC2 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(2);
		ADC3 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(3);
		ADC4 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(4);
		ADC5 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(5);
		
		// Here I can update ADC:s
	}

@Override
	public void run() {
		short[] adcValues;
		
		while(isRunning.get() == true) {
	
			System.out.println("Sending values");
	
			adcValues = AutoWiredFactory.talkToDevice.sendAndReceive();
			
			System.out.println(adcValues);
			// Show the ADC values on the line chart
			ADC0.addData(adcValues[0]
, true, true); // <-- Here I got the error
			ADC1.addData(adcValues[1]
, true, true);
			ADC2.addData(adcValues[2]
, true, true);
			ADC3.addData(adcValues[3]
, true, true);
			ADC4.addData(adcValues[4]
, true, true);
			ADC5.addData(adcValues[5]
, true, true);

			
			// Delay
			try {
				Thread.sleep(stepSelector.getValue().toMillis());
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		
	}

The error becomes.

Exception in thread "Thread-14" java.lang.IllegalStateException: Cannot access state in VaadinSession or UI without locking the session.
	at com.vaadin.flow.server.VaadinSession.checkHasLock(VaadinSession.java:518)
	at com.vaadin.flow.server.VaadinSession.checkHasLock(VaadinSession.java:532)
	at com.vaadin.flow.internal.StateTree.checkHasLock(StateTree.java:408)
	at com.vaadin.flow.internal.StateTree.beforeClientResponse(StateTree.java:339)
	at com.vaadin.flow.dom.Element.lambda$scheduleJavaScriptInvocation$9c7dc614$1(Element.java:1733)
	at com.vaadin.flow.internal.StateNode.runWhenAttached(StateNode.java:837)
	at com.vaadin.flow.dom.Element.scheduleJavaScriptInvocation(Element.java:1732)
	at com.vaadin.flow.dom.Element.callJsFunction(Element.java:1631)
	at com.vaadin.flow.dom.Element.callFunction(Element.java:1589)
	at com.vaadin.flow.component.charts.ProxyChangeForwarder.dataAdded(ProxyChangeForwarder.java:46)
	at com.vaadin.flow.component.charts.model.Configuration.fireDataAdded(Configuration.java:874)
	at com.vaadin.flow.component.charts.model.ListSeries.addData(ListSeries.java:157)
	at se.danielmartensson.jlogger.ui.thread.ChartThread.run(ChartThread.java:168)

In JavaFX, we solve it with Platform.runLater(e -> {code_for_update_chart}); but that’s JavaFX way to update components in threads. So how can I do this in Vaadin?

You need to call

ui.access(() -> {
    // everything that will affect the UI is done here. for example ADC0.addData(...)
});

I’m having a hard time finding the official docs for this, but here’s a [reference]
(https://stackoverflow.com/q/51137436/3441504) from stackoverflow.

You need to keep in mind that the method UI.getCurrent() will return null in all other Threads than the Request Thread. Therefore you’ll need to store the ui instance in the view or in the thread. [Here]
(https://vaadin.com/forum/thread/17341277/17341838) is a good example of that.

Kaspar Scherrer:
You need to call

ui.access(() -> {
    // everything that will affect the UI is done here. for example ADC0.addData(...)
});

I’m having a hard time finding the official docs for this, but here’s a [reference]
(https://stackoverflow.com/q/51137436/3441504) from stackoverflow.

You need to keep in mind that the method UI.getCurrent() will return null in all other Threads than the Request Thread. Therefore you’ll need to store the ui instance in the view or in the thread. [Here]
(https://vaadin.com/forum/thread/17341277/17341838) is a good example of that.

It’s working, but the component wont update. Is there a command to update manually inside a thread for an UI object?

I found the documentation: https://vaadin.com/docs/v14/flow/advanced/tutorial-push-access.html

you also need to have a @Push annotation on the view for it to work.

Edit: I just saw your (now removed) initial response in my inbox, with all your code. The attempt was almost perfect, but you forgot this line in the constructor: this.ui = ui;. This is why it wasnt null in the constructor but in the run() method it was null.

Kaspar Scherrer:
I found the documentation: https://vaadin.com/docs/v14/flow/advanced/tutorial-push-access.html

you also need to have a @Push annotation on the view for it to work.

Edit: I just saw your (now removed) initial response in my inbox, with all your code. The attempt was almost perfect, but you forgot this line in the constructor: this.ui = ui;. This is why it wasnt null in the constructor but in the run() method it was null.

You was very fast there too. Yes! I forgot the this.ui = ui; in the constructor. But still, not updating.

I can post my code again. The start object does not changes at the end. The components updates only when I changes them with my mouse pointer. I want the components to update automatically.

public class ChartThread extends Thread{
	
	private VerticalLayout layout;
	private LineChart lineChart;
	private AtomicBoolean isRunning;
	private HorizontalLayout number_checkbox0;
	private HorizontalLayout number_checkbox1;
	private HorizontalLayout number_checkbox2;
	private HorizontalLayout number_checkbox3;
	private HorizontalLayout number_checkbox4;
	private HorizontalLayout number_checkbox5;
	private NumberField numberField0;
	private Checkbox pwm0;
	private NumberField numberField1;
	private Checkbox pwm1;
	private NumberField numberField2;
	private Checkbox pwm2;
	private NumberField numberField3;
	private Checkbox pwm3;
	private NumberField numberField4;
	private Checkbox pwm4;
	private NumberField numberField5;
	private Checkbox pwm5;
	private HorizontalLayout collection_stepSelector;
	private HorizontalLayout button_counter;
	private ComboBox<Duration> stepSelector;
	private NumberField collection;
	private Button start;
	private NumberField totalSamples;
	private ListSeries ADC0;
	private ListSeries ADC1;
	private ListSeries ADC2;
	private ListSeries ADC3;
	private ListSeries ADC4;
	private ListSeries ADC5;
	private UI ui;
	
	@SuppressWarnings("unchecked")
	public ChartThread(VerticalLayout layout, LineChart lineChart, AtomicBoolean isRunning, UI ui) {
		
		this.layout = layout;
		this.lineChart = lineChart;
		this.isRunning = isRunning;
		
		// These comments displays the indexing of the components objects
		/**
		 * Index for the layout
		 * 0: number_checkbox
		 * 1: number_checkbox
		 * 2: number_checkbox
		 * 3: number_checkbox
		 * 4: number_checkbox
		 * 5: number_checkbox
		 * 
		 * 6: collection_stepSelector
		 * 7: button_counter
		 */
		
		number_checkbox0 = (HorizontalLayout) layout.getComponentAt(0);
		number_checkbox1 = (HorizontalLayout) layout.getComponentAt(1);
		number_checkbox2 = (HorizontalLayout) layout.getComponentAt(2);
		number_checkbox3 = (HorizontalLayout) layout.getComponentAt(3);
		number_checkbox4 = (HorizontalLayout) layout.getComponentAt(4);
		number_checkbox5 = (HorizontalLayout) layout.getComponentAt(5);
		collection_stepSelector = (HorizontalLayout) layout.getComponentAt(6);
		button_counter = (HorizontalLayout)  layout.getComponentAt(7);

		/**
		 * Index for number_checkbox
		 * 0: numberField // PWM adjustment
		 * 1: pwm // Enable PWM and ADC
		 */
		
		numberField0 = (NumberField) number_checkbox0.getComponentAt(0);
		pwm0 = (Checkbox) number_checkbox0.getComponentAt(1);
		
		numberField1 = (NumberField) number_checkbox1.getComponentAt(0);
		pwm1 = (Checkbox) number_checkbox1.getComponentAt(1);
		
		numberField2 = (NumberField) number_checkbox2.getComponentAt(0);
		pwm2 = (Checkbox) number_checkbox2.getComponentAt(1);
		
		numberField3 = (NumberField) number_checkbox3.getComponentAt(0);
		pwm3 = (Checkbox) number_checkbox3.getComponentAt(1);
		
		numberField4 = (NumberField) number_checkbox4.getComponentAt(0);
		pwm4 = (Checkbox) number_checkbox4.getComponentAt(1);
		
		numberField5 = (NumberField) number_checkbox5.getComponentAt(0);
		pwm5 = (Checkbox) number_checkbox5.getComponentAt(1);
		
		/**
		 * Index for collection_stepSelector
		 * 0: stepSelector // Sample time
		 * 1: collection // How many samples we want to have
		 */
	
		stepSelector = (ComboBox<Duration>) collection_stepSelector.getComponentAt(0);
		collection = (NumberField) collection_stepSelector.getComponentAt(1);
		
		/**
		 * Index for button_counter
		 * 0: start // Start button, also close button as well
		 * 1: label // No need this
		 * 2: totalSamples // How many samples we have right now
		 */
		start = (Button) button_counter.getComponentAt(0);
		totalSamples = (NumberField) button_counter.getComponentAt(2);
		totalSamples.setValue((double) 0);
		
		// Get the series from the chart too
		ADC0 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(0);
		ADC1 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(1);
		ADC2 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(2);
		ADC3 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(3);
		ADC4 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(4);
		ADC5 = (ListSeries) lineChart.getChart().getConfiguration().getSeries().get(5);
		
		System.out.println(ui == null); // FALSE
		this.ui = ui;
	}

	@Override
	public void run() {
		short[] pwmValues = new short[6]
; 
		
		
		while(isRunning.get() == true) {
			
			// If we have enough of data samples
			if(totalSamples.getValue() > collection.getValue()) {
				isRunning.set(false);
				System.out.println("Break...");
				break;
			}
			System.out.println("Sending values");
			// Send PWM values and get ADC values
			pwmValues[0]
 = numberField0.getValue().shortValue();
			pwmValues[1]
 = numberField1.getValue().shortValue();
			pwmValues[2]
 = numberField2.getValue().shortValue();
			pwmValues[3]
 = numberField3.getValue().shortValue();
			pwmValues[4]
 = numberField4.getValue().shortValue();
			pwmValues[5]
 = numberField5.getValue().shortValue();
			AutoWiredFactory.talkToDevice.setPwmValues(pwmValues);
			AutoWiredFactory.talkToDevice.setSendState((byte) 0); // Only PWM values
			
			// Show the ADC values on the line chart
			System.out.println(ui == null); // TRUE
			ui.access(() -> {
				short[] adcValues = AutoWiredFactory.talkToDevice.sendAndReceive();
				System.out.println("ADC values[0]
: " + adcValues[0]
);
				System.out.println("ADC values[1]
: " + adcValues[1]
);
				System.out.println("ADC values[2]
: " + adcValues[2]
);
				System.out.println("ADC values[3]
: " + adcValues[3]
);
				System.out.println("ADC values[4]
: " + adcValues[4]
);
				System.out.println("ADC values[5]
: " + adcValues[5]
);
				ADC0.addData(adcValues[0]
, true, true);
				ADC1.addData(adcValues[1]
, true, true);
				ADC2.addData(adcValues[2]
, true, true);
				ADC3.addData(adcValues[3]
, true, true);
				ADC4.addData(adcValues[4]
, true, true);
				ADC5.addData(adcValues[5]
, true, true);
			});
			
			// Delay
			try {
				Thread.sleep(stepSelector.getValue().toMillis());
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			// Count
			ui.access(() -> {
				totalSamples.setValue(totalSamples.getValue() + 1);
				System.out.println("Count:" + totalSamples.getValue());
			});
		}
		
		// Ending
		ui.access(() -> {
			start.setText("Start"); // THIS NOT HAPPENING.
			System.out.println("Setting the start button text to start");
		});
		
	}
	
}

That is indeed very strange. Can you run this minified example and verify this as well in a thread with less code?

public class ChartThread extends Thread {
    private UI ui;
    public ChartThread (UI ui) {
	    system.out.printl("Constructor - ui == null: "+ ui == null);
		this.ui = ui;
	}
	
	@Override
	public void run() {
	    system.out.printl("Run - ui == null: "+ ui == null);
	}
}

And if that really doesnt work,you should show how you use the ChartThread (where and how do you initialize it? how do you update isRunning?).

There is also another way of using ui in a background thread - I made a small helper class for this which looks like this:

public class VaadinBackgroundThread {
    public static Thread createThread(Runnable runnable){
        final UI ui = UI.getCurrent();
        final VaadinSession session = VaadinSession.getCurrent();
        final VaadinService service = VaadinService.getCurrent();
        if (ui == null || session == null || service == null) {
            throw new RuntimeException("Neither UI nor VaadinSession nor VaadinService can be null at this point!");
        }
        return new Thread(() -> {
            UI.setCurrent(ui);
            VaadinSession.setCurrent(session);
            VaadinService.setCurrent(service);
            runnable.run();
        });
    }
}

By setting the CurrentUI within the new Thread, you can actually call UI.getCurrent() inside the runnable (same with VaadinSession and VaadinService). And you would use it like this

Thread chartThread = VaadinBackgroundThread.createThread(() -> {
    while(isRunning.get() == true) {
	    ...
		UI.getCurrent().access(() -> {
			...
		});
	}
});
// TODO: chartThread.setUncaughtExceptionHandler(..)
chartThread.start();

do this in the view with the charts. You can now also directly access all the input fields and don’t have to extrapolate them out of a layout.

Disclaimer: This is not the standard way of doing it, and it is not mentioned anywhere. The “best” way of doing it would be like you did in your last posted code.

Kaspar Scherrer:
That is indeed very strange. Can you run this minified example and verify this as well in a thread with less code?

public class ChartThread extends Thread {
    private UI ui;
    public ChartThread (UI ui) {
	    system.out.printl("Constructor - ui == null: "+ ui == null);
		this.ui = ui;
	}
	
	@Override
	public void run() {
	    system.out.printl("Run - ui == null: "+ ui == null);
	}
}

And if that really doesnt work,you should show how you use the ChartThread (where and how do you initialize it? how do you update isRunning?).

There is also another way of using ui in a background thread - I made a small helper class for this which looks like this:

public class VaadinBackgroundThread {
    public static Thread createThread(Runnable runnable){
        final UI ui = UI.getCurrent();
        final VaadinSession session = VaadinSession.getCurrent();
        final VaadinService service = VaadinService.getCurrent();
        if (ui == null || session == null || service == null) {
            throw new RuntimeException("Neither UI nor VaadinSession nor VaadinService can be null at this point!");
        }
        return new Thread(() -> {
            UI.setCurrent(ui);
            VaadinSession.setCurrent(session);
            VaadinService.setCurrent(service);
            runnable.run();
        });
    }
}

By setting the CurrentUI within the new Thread, you can actually call UI.getCurrent() inside the runnable (same with VaadinSession and VaadinService). And you would use it like this

Thread chartThread = VaadinBackgroundThread.createThread(() -> {
    while(isRunning.get() == true) {
	    ...
		UI.getCurrent().access(() -> {
			...
		});
	}
});
// TODO: chartThread.setUncaughtExceptionHandler(..)
chartThread.start();

do this in the view with the charts. You can now also directly access all the input fields and don’t have to extrapolate them out of a layout.

Disclaimer: This is not the standard way of doing it, and it is not mentioned anywhere. The “best” way of doing it would be like you did in your last posted code.

hi! I have read your message. I will give you a reply later.

Kaspar Scherrer:
I found the documentation: https://vaadin.com/docs/v14/flow/advanced/tutorial-push-access.html

you also need to have a @Push annotation on the view for it to work.

Edit: I just saw your (now removed) initial response in my inbox, with all your code. The attempt was almost perfect, but you forgot this line in the constructor: this.ui = ui;. This is why it wasnt null in the constructor but in the run() method it was null.

Hi! I tried @Push annotation in my MainView and now the view updates! Thank you!