2012-10-10 35 views
6

Tôi đầu hàng. Hãy thử một vài tuần ngay bây giờ để tìm hiểu xem dữ liệu nối tiếp nào đã được nhận một phần dữ liệu nối tiếp của mã của tôi bị chặn. Lập trình lần đầu trong Java. Có khoảng 15 năm kinh nghiệm lập trình micros và tôi được sử dụng để giải quyết các vấn đề của riêng tôi nhưng điều này vượt quá điểm mà ở đó rằng chiến thuật là hiệu quả. Ứng dụng của tôi bao gồm hai tệp.Đang cập nhật đồ họa

Một tệp xuất phát từ dự án RXTX và bắt dữ liệu nối tiếp được gửi trong một số gói hai lần một giây. Điều này hoạt động giống như một sự quyến rũ (mất một thời gian) và tôi có thể thấy rằng dữ liệu bị bắt là chính xác và ổn định.

Tệp khác là đồ họa và bao gồm khoảng 80 menu nơi người dùng cuối có thể đọc và đôi khi viết giá trị. Việc điều hướng được thực hiện bằng các sự kiện chuột trên các nút và thanh cuộn cho đến nay. Phần này cũng hoạt động như mong muốn. Giá trị có thể được đọc, thay đổi và lưu vv ..

Phần mà tôi bị kẹt là giá trị cập nhật từ tệp nối tiếp không bao giờ cập nhật màn hình đồ họa. Đã cố gắng làm theo hàng trăm ví dụ và hướng dẫn (nhiều từ trang web này) mà không có may mắn.

Khái niệm về ngôn ngữ liên quan đến đối tượng là mới đối với tôi và vẫn khá khó hiểu. Khá chắc chắn vấn đề của tôi liên quan đến thừa kế và các lớp học. Chủ đề là một ứng cử viên khác ... Đã cắt giảm mã xuống kích thước nhỏ nhất vẫn chạy và trình bày vấn đề của tôi và hy vọng ai đó có thể thấy những gì sai.

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(); 
    } 
    } 
} 

Và đồ họa nộp

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

không bao giờ bàn giao, không bao giờ đầu hàng – mKorbel

+0

Tôi không quen với việc xoay vòng, nhưng bạn có thể giải thích mối quan hệ giữa các cuộc gọi phương thức không? Đó là một chút khó khăn để giải thích những gì tôi không nhận được: Ở nơi đầu tiên 'SerialComm' gọi' dspUpdate() '. Phương thức này sẽ gọi 'repaint' (tốt tôi nghĩ), gọi lại' paintComponent' gọi 'dspUpdate' là gì? – phineas

+0

@phineas s/ông có vấn đề với Concurency trong Swing, có tất cả các bản cập nhật cho GUI phải được thực hiện trên EDT, – mKorbel

Trả lời

2

Vấn đề lớn nhất mà bạn đang chạy vào là đưa mọi thứ vào lớp GUI. Hãy thử để tách riêng mô hình của bạn (công cụ giao tiếp nối tiếp nối tiếp) từ giao diện người dùng của bạn (công cụ GUI khá), và bạn sẽ tiết kiệm cho mình rất nhiều đau đầu. Trong ví dụ này, tôi đã cố gắng làm điều đó cho bạn - nó nằm trong một tệp, nhưng có lẽ bạn nên tách nó thành 3: Mô hình, Chế độ xem và Điều khiển (điều khiển là giao tiếp giữa mô hình và chế độ xem).

Nếu bạn thêm mã Giao tiếp nối tiếp (mà bạn nói đang hoạt động) vào Mô hình thay vì chuỗi mẫu, bạn sẽ có thể giao tiếp giữa chế độ xem và mô hình mà không gặp quá nhiều rắc rối. Tôi đã cố gắng giữ lại càng nhiều mã của bạn càng tốt.

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 Tôi muốn sử dụng 'SwingWorker', nhưng một thread riêng biệt cho IO nối tiếp là điều cần thiết; xem thêm [answer] này (http://stackoverflow.com/a/12731752/230513). – trashgod

+0

@Nick Rippe Cảm ơn một triệu! Mã nối tiếp "thực" liên quan đến việc lưu ~ 200 byte vào vào bộ đệm được chia sẻ bởi phần đồ họa. Điều này xảy ra mỗi 500ms để sự kiện đó sẽ là "bộ đếm thời gian" của tôi. Cần phải làm mới các giá trị "ẩn" trong bộ đệm có thể được hiển thị sau này khi cuộn qua các menu. Điều này có xảy ra trong mã bạn đã cung cấp không? Mã gốc của tôi là ~ 9000 dòng nên sẽ mất một thời gian để đưa mọi thứ trở lại theo đề xuất của bạn. Có lẽ sẽ quay lại vào ngày mai với một số câu hỏi trên đường đi. Cần đi ngủ ngay bây giờ ... /Richard – user1735586

+0

Bạn sẽ có thể nhồi nhét những giá trị khác vào cùng một người nghe - chỉ cần thêm nhiều dòng như thế này để chuyển thông tin từ Model the View. 'Control.this.screen.modelRefreshInfo = Control.this.model.count;' –