package com.app;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.*;
public class AGVClient extends JFrame {
private final JLabel videoLabel = new JLabel("视频流加载中...", SwingConstants.CENTER);
private final JPanel mapPanel = new MapPanel(); // 自定义面板用于地图和路径绘制
private BufferedImage currentImage = null;
private BufferedImage mapImage = null;
private Socket videoSocket, mapSocket;
private DataInputStream videoIn;
private PrintWriter mapOut;
private BufferedReader mapIn;
private int gridSize = 20; // 模拟地图缩放比例(可调)
private Point startPoint = null, endPoint = null;
private List<Point> pathPoints = new ArrayList<>();
public AGVClient() {
setTitle("AGV远程监控客户端");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1200, 600);
setLayout(new BorderLayout());
// 左侧:视频流
videoLabel.setPreferredSize(new Dimension(640, 480));
videoLabel.setOpaque(true);
videoLabel.setBackground(Color.BLACK);
add(new JScrollPane(videoLabel), BorderLayout.WEST);
// 右侧:地图面板
mapPanel.setPreferredSize(new Dimension(500, 500));
mapPanel.setBackground(Color.LIGHT_GRAY);
mapPanel.setBorder(BorderFactory.createTitledBorder(" 导航地图"));
mapPanel.addMouseListener(new MapClickListener());
add(new JScrollPane(mapPanel), BorderLayout.EAST);
connectToServer();
startVideoThread();
startMapListener();
}
private void connectToServer() {
try {
// 连接视频流
videoSocket = new Socket("localhost", 6000); // 替换为实际IP
videoIn = new DataInputStream(videoSocket.getInputStream());
System.out.println("🎥 已连接至视频流服务器");
// 连接地图服务
mapSocket = new Socket("localhost", 6001);
mapOut = new PrintWriter(mapSocket.getOutputStream(), true);
mapIn = new BufferedReader(new InputStreamReader(mapSocket.getInputStream()));
System.out.println("🗺️ 已连接至地图服务");
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "无法连接到服务器: " + e.getMessage(),
"连接错误", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
}
private void startVideoThread() {
new Thread(() -> {
try {
while (!videoSocket.isClosed()) {
// 读取图像大小(4字节大端)
byte[] sizeBytes = new byte[4];
int bytesRead = 0;
while (bytesRead < 4) {
bytesRead += videoIn.read(sizeBytes, bytesRead, 4 - bytesRead);
}
int imageSize = ByteBuffer.wrap(sizeBytes).getInt();
// 读取图像数据
byte[] imageBytes = new byte[imageSize];
bytesRead = 0;
while (bytesRead < imageSize) {
bytesRead += videoIn.read(imageBytes, bytesRead, imageSize - bytesRead);
}
// 解码图像
ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes);
BufferedImage img = ImageIO.read(bais);
if (img != null) {
currentImage = img;
videoLabel.setIcon(new ImageIcon(img));
videoLabel.repaint();
}
// 从当前帧生成灰度图作为地图底图(模拟)
generateMapFromFrame(img);
}
} catch (Exception e) {
if (!videoSocket.isClosed())
e.printStackTrace();
JOptionPane.showMessageDialog(this, "视频流中断", "错误", JOptionPane.WARNING_MESSAGE);
}
}).start();
}
private void generateMapFromFrame(BufferedImage frame) {
int w = frame.getWidth() / 4;
int h = frame.getHeight() / 4;
BufferedImage small = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g = small.createGraphics();
g.drawImage(frame.getScaledInstance(w, h, Image.SCALE_SMOOTH), 0, 0, null);
g.dispose();
mapImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int gray = new Color(small.getRGB(x, y)).getRed();
int color = gray < 120 ? 0x000000 : 0xFFFFFF; // 黑色障碍,白色自由区
mapImage.setRGB(x, y, color);
}
}
mapPanel.repaint();
}
private void startMapListener() {
new Thread(() -> {
try {
String line;
while ((line = mapIn.readLine()) != null) {
System.out.println("📡 收到路径: " + line);
JSONObject json = parseJSON(line.trim());
if (json.containsKey("path")) {
List<Point> newPath = new ArrayList<>();
List<List<Integer>> rawPath = (List<List<Integer>>) json.get("path");
for (List<Integer> p : rawPath) {
newPath.add(new Point(p.get(0), p.get(1)));
}
pathPoints = newPath;
mapPanel.repaint();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
class MapPanel extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (mapImage != null) {
g.drawImage(mapImage, 0, 0, getWidth(), getHeight(), null);
// 绘制网格线(可选)
int w = mapImage.getWidth(), h = mapImage.getHeight();
int pw = getWidth() / w, ph = getHeight() / h;
g2d.setColor(Color.LIGHT_GRAY);
for (int i = 0; i <= w; i++) g2d.drawLine(i * pw, 0, i * pw, getHeight());
for (int j = 0; j <= h; j++) g2d.drawLine(0, j * ph, getWidth(), j * ph);
}
// 绘制路径
g2d.setColor(Color.GREEN);
g2d.setStroke(new BasicStroke(3));
for (int i = 0; i < pathPoints.size() - 1; i++) {
Point a = pathPoints.get(i);
Point b = pathPoints.get(i + 1);
g2d.drawLine(a.x * pw(), a.y * ph(), b.x * pw(), b.y * ph());
}
// 绘制起点(蓝色)
if (startPoint != null) {
g2d.setColor(Color.BLUE);
g2d.fillOval(startPoint.x * pw() - 5, startPoint.y * ph() - 5, 10, 10);
}
// 绘制终点(红色)
if (endPoint != null) {
g2d.setColor(Color.RED);
g2d.fillOval(endPoint.x * pw() - 5, endPoint.y * ph() - 5, 10, 10);
}
}
private int pw() { return getWidth() / (mapImage != null ? mapImage.getWidth() : 1); }
private int ph() { return getHeight() / (mapImage != null ? mapImage.getHeight() : 1); }
}
class MapClickListener extends MouseAdapter {
@Override
public void mouseClicked(MouseEvent e) {
if (mapImage == null) return;
int x = e.getX() / mapPanel.pw();
int y = e.getY() / mapPanel.ph();
if (x < 0 || x >= mapImage.getWidth() || y < 0 || y >= mapImage.getHeight()) return;
if (SwingUtilities.isLeftMouseButton(e)) {
startPoint = new Point(x, y);
sendWaypoints();
} else if (SwingUtilities.isRightMouseButton(e)) {
endPoint = new Point(x, y);
sendWaypoints();
}
}
}
private void sendWaypoints() {
if (mapOut == null || mapOut.checkError()) return;
Map<String, Object> msg = new HashMap<>();
if (startPoint != null) {
msg.put("start", Arrays.asList(startPoint.x, startPoint.y));
}
if (endPoint != null) {
msg.put("end", Arrays.asList(endPoint.x, endPoint.y));
}
mapOut.println(new JSONObject(msg).toString());
System.out.println("📤 发送路径请求: " + msg);
}
// 简易 JSON 实现(避免依赖外部库)
static class JSONObject extends LinkedHashMap<String, Object> {
public JSONObject(Map<String, Object> m) { super(m); }
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
Iterator<Map.Entry<String, Object>> it = entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> e = it.next();
sb.append("\"").append(e.getKey()).append("\":");
Object v = e.getValue();
if (v instanceof List) {
sb.append(listToString((List<?>) v));
} else if (v instanceof String) {
sb.append("\"").append(v).append("\"");
} else {
sb.append(v);
}
if (it.hasNext()) sb.append(",");
}
sb.append("}");
return sb.toString();
}
private String listToString(List<?> list) {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);
if (o instanceof List) {
sb.append(listToString((List<?>) o));
} else if (o instanceof String) {
sb.append("\"").append(o).append("\"");
} else {
sb.append(o);
}
if (i < list.size() - 1) sb.append(",");
}
sb.append("]");
return sb.toString();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeel());
} catch (Exception ignored) {}
new AGVClient().setVisible(true);
});
}
}将上述程序换为相同功能android studio的程序
最新发布