0

我是 JavaFx/Concurrency 的新手,所以我在 JavaFX 中的 Concurrency 上阅读了教程,但我仍然对 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来实现。有了这个,您可以定义一个 SimpleStringProperty,它绑定到您的 UI 标签(.bind() 方法)。如果控制器必须触发其消息,您可以设置 StringProperty 并且 UI 将自行更新。您描述的场景可以这样使用:

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

我们被告知,并发通知/等待 级别上的并发等待()/通知()是非常低的级别。您应该尝试使用更高级别的同步方法或助手(人们已经解决了您的问题:))

于 2015-07-10T14:19:21.633 回答