2012-10-10 28 views
6

Teslim oldum. Ne zaman engelleme olduğunu öğrenmek için birkaç hafta boyunca denemek için alınan seri veri kodumun grafik bölümü tarafından güncellenmektedir. Java'da ilk kez programlama. micros programlama konusunda 15 yıllık deneyimim var ve kendi problemlerimi çözmek için kullanıyorum ama bu, taktikin 'un üretken olduğu noktanın ötesine geçiyor. Uygulamam iki dosyadan oluşur.Grafik Güncellemesi

Bir dosya, RXTX projesinden kaynaklanıyor ve her saniye iki defa paketlerinde gönderilen seri verileri yakalıyor. Bu çekicilik gibi çalışır (biraz zaman aldı) ve yakalanan verilerin doğru olduğunu görebiliyorum ve kararlı.

Diğer dosya grafiktir ve son kullanıcının okuduğu ve bazen yazabileceği yaklaşık 80 menüden oluşur. Gezinme, düğmelerinde fare olayları ve şu ana kadar kaydırma çubuğu ile yapılır. Bu bölüm de gerektiği gibi çalışır. Değerler okunabilir, değiştirilebilir ve kaydedilebilir vb.

Takılı kaldığım bölüm, seri dosyasındaki güncellenmiş değerlerin hiçbir zaman grafik ekranını güncellememesidir. Şanssız yüzlerce örnek ve öğreticiyi (bu siteden birçok ) takip etmeyi denediniz.

Nesne ile ilgili diller kavramı benim için yeni ve hala oldukça kafa karıştırıcı. Sorunumun kalıtım ve sınıfları içerdiğinden eminim. Threads başka bir aday ... Hala en küçük boyuta kadar kod aşağı kesim var ve sorunumu sunmak ve birilerinin neyin yanlış görebileceğini umuyoruz.

package components; 

import gnu.io.CommPort; 
import gnu.io.CommPortIdentifier; 
import gnu.io.SerialPort; 
import gnu.io.SerialPortEvent; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import javax.swing.SwingUtilities; 

public class SerialComm extends ScreenBuilder implements java.util.EventListener { 

InputStream in; 

public SerialComm() { 
    super(); 
} 

public interface SerialPortEventListener 
     extends java.util.EventListener { 
} 

void connect(String portName) throws Exception { 
    CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier("COM1"); 
    if (portIdentifier.isCurrentlyOwned()) { 
     System.out.println("Error: Port is currently in use"); 
    } else { 
     CommPortIdentifier.getPortIdentifier("COM1"); 
     System.out.println("" + portName); 
     CommPort commPort = portIdentifier.open("COM1", 2000); 
     if (commPort instanceof SerialPort) { 
      SerialPort serialPort = (SerialPort) commPort; 
      serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_2, SerialPort.PARITY_NONE); 
      InputStream in = serialPort.getInputStream(); 
      OutputStream out = serialPort.getOutputStream(); 
      serialPort.addEventListener(new SerialComm.SerialReader(in)); 
      serialPort.notifyOnDataAvailable(true); 

      (new Thread(new SerialComm.SerialReader(in))).start(); 
      // TX functionality commented for now 
      //    (new Thread(new SerialWriter(out))).start(); 

     } else { 
      System.out.println("Error: Only serial ports are handled by this  example."); 
     } 
    } 
} 

public class SerialReader extends SerialComm implements Runnable, 
     gnu.io.SerialPortEventListener { 

    public SerialReader(InputStream in) { 
     this.in = in; 
    } 

    @Override 
    public void run() { 
    count=11; // just for test. run is normally empty 
    count2=count; // and real code runs within serialEvent() 
    System.out.println("SerialReader " + count); 
    dspUpdate(); // do some desperate stuff in graphics file 
    System.out.println("Post Update " + count); 
    } 

    @Override 
    public void serialEvent(SerialPortEvent event) { 
    System.out.println("SerialEvent"); 
     switch (event.getEventType()) { 
      case SerialPortEvent.DATA_AVAILABLE: 
       try { 
        synchronized (in) { 
         while (in.available() < 0) { 
          in.wait(1, 800000); 
         } //in real code RX data is captured here twice a sec 
        } //and stored into buffers defined in ScreenBuilder 
    //dspUpdate() is called from here to make ScreenBuilder update its screen 
    //That never happens despite all my attempts    
       } catch (IOException e) { 
        System.out.println("IO Exception"); 
       } catch (InterruptedException e) { 
        System.out.println("InterruptedException caught"); 
       } 
     } 
    } 
} 

/* "main" connect PC serial port and start graphic part of application 
* To demonstrate problem with no serial data stream present 
* order of init between serial port and graphics are switched 
*/ 

public static void main(String[] args) { 

    SwingUtilities.invokeLater(new Runnable() { 

     @Override 
     public void run() { 
      ScreenBuilder screen = new ScreenBuilder(); 
      screen.createAndShowGUI(); 
      System.out.println("Created GUI"); 
     } 
    }); 
    try { 
     (new SerialComm()).connect("COM1"); 
    } catch (Exception e) { 
     System.out.println("Error"); 
     e.printStackTrace(); 
    } 
    } 
} 

Ve grafik

package components; 

import java.awt.*; 
import javax.swing.SwingUtilities; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.BorderFactory; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Font; 
import java.awt.Graphics; 
import java.awt.event.*; 

public class ScreenBuilder extends JPanel implements ActionListener { 

public Font smallFont = new Font("Dialog", Font.PLAIN, 12); 
Color screenColor; 
Color lineColor; 
short btn=0; 
short count; 
short count2; 
Button helpButton; 

public static void createAndShowGUI() { 
    System.out.println("Created GUI on EDT? " 
      + SwingUtilities.isEventDispatchThread()); 
    JFrame f = new JFrame("JUST A TEST"); 
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    f.add(new ScreenBuilder()); 
    f.pack(); 
    f.setVisible(true); 
} 

public void dspButton() { 
    setLayout(null);// 
    helpButton = new Button("?"); 
    helpButton.setLocation(217, 8); // set X, Y 
    helpButton.setSize(16, 14); //Set Size X, Y // 
    helpButton.addActionListener(this); 
    add(helpButton); 
    setBackground(Color.black); 
    helpButton.setBackground(Color.black); 
    screenColor = Color.black; 
    helpButton.setForeground(Color.white); 
    lineColor = Color.white; 
} 

@Override 
public void actionPerformed(ActionEvent e) { 
    if (e.getSource() == helpButton) { 
     count2++; 
     System.out.println("Pressed Button "); 
     repaint(); 
    } 
} 

public ScreenBuilder() { 
    setBorder(BorderFactory.createLineBorder(Color.black)); 
} 

@Override 
public Dimension getPreferredSize() { 
    return new Dimension(240, 180); 
} 

public void dspUpdate() { 
    /* 
    * This function is called from SerialComm 
    * Should be called when serial packets have arrived (twice a second) 
    * and update screen with values from serial stream 
    * For now just a test var to validate that values from SerialComm 
    * get to here (they do) 
    */ 
count++; 
System.out.println("Update Count " + count); 
System.out.println("Update Count2 " + count2); 
// revalidate(); // another futile attempt to update screen 
// repaint(); 
} 

@Override 
public void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    g.setColor(lineColor); 
    g.setFont(smallFont); 
    count++; 
    g.drawString("" + count, 130, 20); 
    g.drawString("" + count2, 150, 20); 
    if (btn == 0) { 
     dspButton(); 
     btn = 1; 
    } 
    } 
} 
+4

asla teslim olmaz, asla teslim olmaz – mKorbel

+0

Saldırıya aşina değilim, ancak yöntem çağrıları arasındaki ilişkiyi açıklayabilir misiniz? Ne elde edemediğimi açıklamak biraz zor: İlk sırada 'SerialComm'' dspUpdate() 'diyor. Bu yöntem, "repaint" (iyi sanırım), "dspUpdate" olarak adlandırılan "paintComponent" çağrılarını yeniden çağırır mı? – phineas

+0

@phineas s/o Swing'teki Concurency ile ilgili bir sorunu var, GUI ile ilgili tüm güncellemeler EDT üzerinde yapılmalı, – mKorbel

cevap

2

GUI sınıfa şeyi koyuyor yaşamaya devam edip en büyük sorun dosya. Modelinizi (arka uç seri iletişim araçlarını) ön uçtan (güzel GUI nesneleri) ayırmaya çalışın ve kendinizi çok fazla başağrıdan koruyacaksınız. Örnekte bunu sizin için yapmaya çalıştım - tek bir dosyada, ancak bunu 3: Model, Görünüm ve Kontrol olarak ayırmalısınız (kontrol model ve görünüm arasında iletişim kurar). yerine örnek dişin Modeli (eğer çaba harcadığını belirtti) sizin Seri Haberleşme kodu eklerseniz

, çok fazla uğraşmadan görünümü ve model arasındaki iletişim kurabilmesi gerekir. Kodunuzun çoğunu olabildiğince korumaya çalıştım.

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.*; 

public class TranslucentWindow { 

    public static void main(String[] args) { 

     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       try { 
        View screen = new View(); 
        System.out.println("Created GUI"); 
        Model model = new Model(); 

        Control c = new Control(screen, model); 
       } catch (Exception e) { 
        System.out.println("Error"); 
        e.printStackTrace(); 
       } 
      } 
     }); 
    } 

    //Only cares about the backend. Simplified because you said all the backend code was working right. 
    public static class Model{ 

     //Data that was updated - you can change this to whatever you want. 
     public String count; 
     //Listener that notifies anyone interested that data changed 
     public ActionListener refreshListener; 

     public void run() { 
      //As a sample, we're updating info every 1/2 sec. But you'd have your Serial Listener stuff here 
      Thread t = new Thread(new Runnable(){ 
       @Override 
       public void run() { 
        int i = 0; 
        while(true){ 
         dspUpdate(i++); 
         try { 
          Thread.sleep(500); 
         } catch (InterruptedException e) { 
          e.printStackTrace(); 
         } 
        } 
       }}); 
      t.start(); 
     } 

     //Update data and notify your listeners 
     public void dspUpdate(int input) { 
      count = String.valueOf(input); 
      System.out.println("Update Count " + count); 
      refreshListener.actionPerformed(new ActionEvent(this, input, "Update")); 
     } 

    } 


    //Only cares about the display of the screen 
    public static class View extends JPanel { 

     public Font smallFont = new Font("Dialog", Font.PLAIN, 12); 
     Color screenColor; 
     Color lineColor; 
     short btn=0; 
     String modelRefreshInfo; 
     int buttonPressCount; 
     Button helpButton; 

     public View(){ 
      //Build Panel 
      dspButton(); 

      //Create and show window 
      System.out.println("Created GUI on EDT? "+ SwingUtilities.isEventDispatchThread()); 
      JFrame f = new JFrame("JUST A TEST"); 
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
      f.add(this); 
      f.pack(); 
      f.setVisible(true); 
     } 

     public void dspButton() { 
      setLayout(null);// 
      helpButton = new Button("?"); 
      helpButton.setLocation(217, 8); // set X, Y 
      helpButton.setSize(16, 14); //Set Size X, Y // 
      add(helpButton); 
      setBackground(Color.black); 
      helpButton.setBackground(Color.black); 
      screenColor = Color.black; 
      helpButton.setForeground(Color.white); 
      lineColor = Color.white; 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(240, 180); 
     } 

     @Override 
     public void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      g.setColor(lineColor); 
      g.setFont(smallFont); 
      g.drawString("ModelUpdates: " + modelRefreshInfo, 10, 20); 
      g.drawString("RefreshCount: " + buttonPressCount, 10, 40); 
      if (btn == 0) { 
       dspButton(); 
       btn = 1; 
      } 
     } 
    } 

    //Links up the view and the model 
    public static class Control{ 
     View screen; 
     Model model; 

     public Control(View screen, Model model){ 
      this.screen = screen; 
      //Tells the screen what to do when the button is pressed 
      this.screen.helpButton.addActionListener(new ActionListener(){ 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        //Update the screen with the model's info 
        Control.this.screen.buttonPressCount++; 
        System.out.println("Pressed Button "); 
        Control.this.screen.repaint(); 
       } 
      }); 

      this.model = model; 
      //Hands new data in the model to the screen 
      this.model.refreshListener = new ActionListener(){ 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        //Update the screen with the model's info 
        Control.this.screen.modelRefreshInfo = Control.this.model.count; 
        System.out.println("Model Refreshed"); 
        Control.this.screen.repaint(); 
       } 
      }; 

      //Starts up the model 
      this.model.run(); 
     }  
    } 
} 
+0

+1 'SwingWorker' kullanırdım, ancak seri IO için ayrı bir iş parçacığı gereklidir; ayrıca bkz. bu [cevap] (http://stackoverflow.com/a/12731752/230513). – trashgod

+0

@Nick Rippe Bir milyona teşekkürler! "Gerçek" seri kod, grafik bölümü tarafından paylaşılan tamponlara ~ 200 gelen bayt kaydetmeyi içerir. Bu her 500 ms'de olur, böylece etkinlik benim "zamanlayıcı" olur. Ayrıca, daha sonra menüler arasında gezinirken görüntülenebilecek olan arabelleklerde "gizli" değerleri de yenilemeye ihtiyaç duyarsınız. Bu, sağladığınız kodda mı olacak? Orijinal kodum ~ 9000 satır oldu, bu yüzden önerilerinize göre işleri geri koymak biraz zaman alacaktır. Muhtemelen yarın yol boyunca bazı sorularla geri dönün. Şimdi yatağa gitmeye ihtiyacımız var ... /Richard – user1735586

+0

Diğer değerleri aynı dinleyiciye aktarabilmeniz gerekir - bu bilgiyi View (Görünüm) modelinden geçiren daha fazla satır eklemeniz yeterlidir. 'Control.this.screen.modelRefreshInfo = Control.this.model.count;' –