0

私は JavaFx/Concurrency を初めて使用するので、Concurrency in JavaFXのチュートリアルを読みましたが、JavaFX Gui でのバックグラウンド スレッドの実装についてまだ少し混乱しています。

いくつかのシリアル デバイス (JSSC-2.8 を使用) とインターフェイスし、それらのデバイスからの応答に基づいて GUI を更新する小さな GUI を作成しようとしています。しかし、メッセージが書き込まれてからデバイスが応答するまでにはタイムラグがあり、任意の時間 Thread.sleep() を使用することは、私がプログラムする信頼できる方法ではありませんでした。代わりに、同時実行パッケージの wait() メソッドと notify() メソッドを (すべての適切な同期と共に) 使用したいのですが、実装方法がわかりません。私が最初にしたことは、タスク内に別のスレッドを作成し、メッセージを書き込んで応答を待ち、いくつかのバインディングを使用して GUI を更新することでした。最後にコードを含めました。実装しようとしている擬似コードの短い形式を次に示します。

start Task:
  connect to serial devices
  synchronized loop: 
    send messages
    wait() for event to fire
      notify()

しかし、何が起こっているのかというと、wait() を呼び出すとすぐに、アプリケーション全体がアイドル状態になり、(応答が発生してイベントが発生した後) notify() が呼び出されると、レシピで中断したところから続行されません( ) ループ、またはそのことについての startTdk() ループ、それはただのアイドル状態です。スレッドを間違って実装しましたか? wait() を呼び出しているときに、EventDispatch または JavaFX アプリケーション スレッドが一時停止する可能性はありますか?

質問が明確であることを願っています。明確化が必要な場合は、投稿を更新できます。

public class OmicronRecipe extends Service<String> implements Runnable{

private final String SEPERATOR=";";
private final Tdk tdk;
private final Pvci pvci;
private final SimpleStringProperty data = new SimpleStringProperty(""); 
private final Float MAX_V = 26.0f,UHV=1e-8f;

private boolean isTdkOn=false, isPvciOn=false;
private String power;
private Float temp,press,maxT, setT;
private int diffMaxT,diffP,diffPow, diffT, index=0;

public OmicronRecipe(){
    tdk = new Tdk("COM4");
    pvci = new Pvci("COM5");
}

private synchronized void recipe(){
        while (true){
            try {
                sendMessages();
                data.set(power+SEPERATOR+temp+SEPERATOR+press);
                calcDiffs();
                if (diffPow < 0){
                    if(diffMaxT < 0){
                        if(diffT < 0){
                            if (diffP < 0){
                                if(!rampPow()){
                                    //Max Power reached
                                }
                            }else{
                                //Wait for pressure drop
                            }
                        }
                    }else{
                        //Wait until quit
                    }
                }else{
                    //Max power reached
                }
                Thread.sleep(5000);
            } catch (InterruptedException ex) {
                Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
}

private synchronized boolean rampPow(){
    boolean isRamped=false;
    Float setPow = tdk.getSetPow(index), curPow;
    setT = tdk.getSetT(index);
    curPow = Float.parseFloat(power);
    if(curPow.compareTo(setPow) < 0){
        do{
            curPow += 0.1f;
            tdk.sendMessage("PV "+curPow+"\r");
            try {
                wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
            }
            curPow = Float.parseFloat(power);
        }while(curPow.compareTo(setPow) < 0);
        index++;
        isRamped=true;
    }
    return isRamped;
}

public synchronized boolean connect(){
    if(!isTdkOn && !isPvciOn){
        isTdkOn = tdk.connect();
        isPvciOn = pvci.connect();
    }
    return isTdkOn && isPvciOn;
}

public synchronized boolean disconnect(){
    if(tdk!=null && pvci !=null){
        isTdkOn = tdk.disconnect();
        isPvciOn = pvci.disconnect();
    }
    return !isTdkOn && !isPvciOn;
}

public synchronized StringProperty getData(){
    return data;
}

public void setMaxT(Float maxT){
    this.maxT = maxT;
}

private synchronized void calcDiffs(){
    Float pow = Float.parseFloat(power);
    diffPow = pow.compareTo(MAX_V);
    diffMaxT = temp.compareTo(maxT);
    diffT = temp.compareTo(100f);
    diffP = press.compareTo(UHV);
}

private synchronized void setListeners(){
    tdk.getLine().addListener((ov,t, t1)-> {
        synchronized (this){
            System.out.println("New Power: "+t1);
            power = t1;
            this.notify();
        }
    });
    pvci.getLine().addListener((ov,t,t1) ->{
        synchronized (this){
        String[] msg = t1.split(SEPERATOR);
        if(msg.length == 2){
            switch(msg[0]){
                case "temperature":
                    System.out.println("Temperaute");
                    temp = Float.parseFloat(msg[1]);
                    break;
                case "pressure":
                    System.out.println("Pressure");
                    press = Float.parseFloat(msg[1]);
                    break;
                default:
                    System.out.println("Nothing; Something went wrong");
                    break;
            }
        }

            this.notify();
        }
    });
}

private synchronized void sendMessages(){
        try {
            tdk.sendMessage("PV?\r");
            this.wait();
            pvci.sendMessage("temperature");
            this.wait();
            pvci.sendMessage("pressure");
            this.wait();
        } catch (InterruptedException ex) {
            Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
        }
}

private synchronized boolean startTdk(){
    boolean isOut=false;
        if(isTdkOn){
            try {
                tdk.sendMessage("ADR 06\r");
                this.wait();
                System.out.println("Power: "+power);
                if(power.equals("OK")){
                    tdk.sendMessage("OUT?\r");
                    this.wait();
                    if(power.equals("OFF")){
                        tdk.sendMessage("OUT ON\r");
                        this.wait();
                        isOut = power.equals("ON");
                    }
                    else{
                        isOut = power.equals("ON");
                    }
                }
            } catch (InterruptedException ex) {
                Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return isOut;
}

@Override
protected Task<String> createTask() {

    return new Task<String>() {
          @Override
          protected String call() throws IOException{
            new Thread(new OmicronRecipe()).start();
            return "";
          }
      };

}

@Override
public void run() {
    if (connect()){
        setListeners();
        if(startTdk()){
            recipe();
        }
    }
}
}

Pvci クラスは含めません。これは Tdk クラスの単なるコピーですが、そのマシンと通信するための特定のメッセージ シーケンスを備えているためです。

public class Tdk {

private SerialPort tdkPort;
private final String portName;
private StringBuilder sb = new StringBuilder("");;
private final StringProperty line = new SimpleStringProperty("");
private final HashMap<Float,Float> calibMap;
private ArrayList<Float> list ;
private boolean isEnd=false;

public Tdk(String portName){
    this.portName = portName;
    System.out.println("TDK at "+portName);
    calibMap = new HashMap();
    setMap();
}

public synchronized boolean connect(){
    tdkPort = new SerialPort(portName);
    try {
        System.out.println("Connecting");
        tdkPort.openPort();
        tdkPort.setParams(9600,
                SerialPort.DATABITS_8,
                SerialPort.STOPBITS_1,
                SerialPort.PARITY_NONE);
        tdkPort.setEventsMask(SerialPort.MASK_RXCHAR);
        tdkPort.addEventListener(event -> {
            if(event.isRXCHAR()){
                if(event.getPortName().equals(portName)){
                    try {
                        if(!isEnd){
                            int[] str = tdkPort.readIntArray();
                            if(str!=null)
                                hexToString(str);    
                        }
                        if(isEnd){
                            System.out.println("Here: "+sb.toString());
                            isEnd=false;
                            String d = sb.toString();
                            sb = new StringBuilder("");
                            line.setValue(d);

                        }
                    } catch (SerialPortException e) {
                            Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
                    }
                }
            }
        });
    } catch (SerialPortException e) {
            Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
    }
    return tdkPort !=null && tdkPort.isOpened();
}

public synchronized boolean disconnect(){
    if(tdkPort!=null) {
        try {
            tdkPort.removeEventListener();
            if (tdkPort.isOpened())
                    tdkPort.closePort();
        } catch (SerialPortException e) {
                Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
        }
        System.out.println("Disconnecting");
    }
    return tdkPort.isOpened();
}

public synchronized void sendMessage(String message){
    try {
        tdkPort.writeBytes(message.getBytes());
    } catch (SerialPortException e) {
            Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
    }
}

private void setMap(){
    calibMap.put(1.0f, 25.0f);
    calibMap.put(7.0f, 125.0f);
    calibMap.put(9.8f, 220.0f);
    list = new ArrayList(calibMap.keySet());
}

public Float getSetPow(int index){
    return list.get(index);
}

public Float getSetT(int index){
    return calibMap.get(list.get(index));
}

public synchronized StringProperty getLine(){
    return line;
}

private synchronized void hexToString(int[] hexVal){
    for(int i : hexVal){
        if(i != 13){
            sb.append((char)i);
        }else{
            isEnd=true;
        }
    }
    System.out.println("Turning: "+Arrays.toString(hexVal)+" to String: "+sb.toString()+" End: "+isEnd);
}
4

1 に答える 1

2

氷結

UI がフリーズするのは、おそらく FX アプリケーション スレッドを待っているためです。これを解決するには、さまざまな方法があります。


JavaFX アプリケーションスレッド

一部の作業を FX アプリケーション スレッドに委任できます。したがって、Platform.runLaterを参照してください。

このスレッドですべてを実行できるわけではありませんが、たとえば、DeviceController では、メッセージが表示されるまで待ってから、Platform.runLater() を呼び出してフィールドを更新できます (そのため、フィールドをコントローラーに渡す必要があります)。


DataBinding あなたが説明していることは、DataBindingでも実現できます。これにより、UI ラベル (.bind() メソッド) にバインドされる SimpleStringProperty を定義できます。コントローラーがメッセージを発行する必要がある場合は、StringProperty を設定すると、UI が自動的に更新されます。あなたが説明したシナリオは、次のように使用できます。

start Task:
    connect to serial devices
    synchronized loop: 
        send messages
        wait() for event to fire
        **updateDate the DataBounded fields**

私たちは、並行性 notify/wait レベルの並行性 wait()/notify() は非常に低レベルであると教えられています。より高いレベルの同期メソッドまたはヘルパーを使用して作業するようにしてください (人々がすでに問題を解決している場合:))

于 2015-07-10T14:19:21.633 に答える