use*_*586 6 java concurrency inheritance swing serial-port
我投降.现在尝试几周,找出阻止接收的串行数据被我的代码的图形部分更新的内容.首次使用Java编程.有大约15年的微程序编程经验,我习惯于解决自己的问题,但这超出了这种策略的效率.我的应用程序包含两个文件.
一个文件源于RXTX项目,并且每秒两次捕获以几个数据包发送的串行数据.这就像一个魅力(花了一些时间),我可以看到捕获的数据是正确和稳定的.
另一个文件是图形文件,由大约80个菜单组成,最终用户可以阅读并有时写入值.到目前为止,导航是通过按钮和滚动条上的鼠标事件完成的.这部分也可以正常工作.可以读取,更改和保存值等.
我遇到的部分是来自串行文件的更新值永远不会更新图形屏幕.试图跟随数百个示例和教程(很多来自这个网站)没有运气.
对象相关语言的概念对我来说是新的,但仍然令人困惑.很确定我的问题涉及继承和类.线程是另一个候选人......已经将代码减少到仍然会运行并呈现我的问题的最小尺寸,并希望有人可以看到什么是错误的.
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
和图形文件
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
您遇到的最大问题是将所有内容放入 GUI 类中。尝试将你的模型(后端串行通信内容)与前端(漂亮的 GUI 内容)分开,这样你就会省去很多麻烦。在示例中,我尝试为您执行此操作 - 它位于一个文件中,但您可能应该将其分为 3 个:模型、视图和控制(控制是模型和视图之间进行通信的内容)。
如果您将串行通信代码(您所说的有效)添加到模型而不是示例线程中,那么您应该能够在视图和模型之间进行通信,而不会遇到太多麻烦。我试图尽可能多地保留您的代码。
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)