diff --git a/app/include/pd_csg.h b/app/include/pd_csg.h index a9841bd..b77aeeb 100755 --- a/app/include/pd_csg.h +++ b/app/include/pd_csg.h @@ -178,6 +178,7 @@ typedef struct char buf_send[1500]; // 发送缓冲区. char buf_recv[1500]; // 接收缓冲区. struct sockaddr_in server; // 服务器地址. + struct sockaddr_in wave_svr; int32_t server_ip; // server ip. uint16_t server_port; // server port. uint8_t is_connect; // 是否连接上服务器. diff --git a/app/include/pd_dbg.h b/app/include/pd_dbg.h index 90b8d78..199ab59 100755 --- a/app/include/pd_dbg.h +++ b/app/include/pd_dbg.h @@ -64,7 +64,7 @@ enum DebugCommand DEBUG_DEVICE_STATUS = 8, /* 查询设备状态 */ DEBUG_TIME_SET = 9, /* 对时 */ DEBUG_REBOOT = 10, /* 重启 */ - + DEBUG_SET_WAVE_ADDRESS = 11, /* 设置波形地址 */ }; #define BITMAP_SAVE_FILE (1 << 0) @@ -182,6 +182,12 @@ typedef struct { uint16_t index; // 应答包序号. } dbg_upgrade_ack_t; + +typedef struct +{ + char ipv4[16]; // wave IP. + uint16_t port; // wave port. +} dbg_wave_addr_t; /* Exported macro ------------------------------------------------------------*/ diff --git a/app/lib/a_process/pd_csg.c b/app/lib/a_process/pd_csg.c index c18b2ad..adecc96 100755 --- a/app/lib/a_process/pd_csg.c +++ b/app/lib/a_process/pd_csg.c @@ -304,6 +304,17 @@ void _csg_send_data(uint8_t cmd_type, uint8_t cmd, char *pkt, int32_t len) } } +void _csg_send(int fd, char *pkt, uint16_t len, struct sockaddr_in sk_server) +{ + int32_t rv = 0; + rv = sendto(fd, pkt, len, 0, (struct sockaddr*)&sk_server, sizeof(sk_server)); + if (rv < 0) + { + DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + usleep(50); + } +} + /* 与后台连接断开 */ void _csg_disconnect_set(const char *message) { @@ -536,7 +547,6 @@ int32_t _csg_trend_send(pd_trend_port_t *ptrend) uint8_t port_idx; uint32_t nums; uint32_t len; - int32_t rv = 0; int i; int fd; if (!ch) @@ -609,12 +619,31 @@ int32_t _csg_trend_send(pd_trend_port_t *ptrend) image_head->nanosecond = header->nano_sec; memcpy(pkt + sizeof(csg_pkt_head_t) + sizeof(csg_real_image_t), data_point, image_head->record_points*sizeof(int16_t)); - rv = sendto(fd, pkt, pkt_head->len, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); - if (rv < 0) + //rv = sendto(fd, pkt, pkt_head->len, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); + //if (rv < 0) + //{ + // DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + // usleep(50); + //} + struct sockaddr_in sk_server; + if (csg.wave_svr.sin_addr.s_addr == 0) { - DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); - usleep(50); + sk_server = csg.server; } + else + { + sk_server = csg.wave_svr; + } + + char ip_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(sk_server.sin_addr), ip_str, sizeof(ip_str)); + printf("IP Address: %s\n", ip_str); + + // 获取端口号,使用ntohs()函数转换回主机字节序 + unsigned short port = ntohs(sk_server.sin_port); + printf("Port: %d\n", port); + + _csg_send(fd, pkt, pkt_head->len, sk_server); } else if(csg.new_data_flag[port_idx] == TRUE) { @@ -1003,7 +1032,7 @@ int32_t _csg_config_set_recv(char *pkt) pd_config.config.ch_en_mask = config->ch_en_mask; if(config->sync_mode < 4) { - pd_config.config.pt_B_sync_mode = config.sync_mode; + pd_config.config.pt_B_sync_mode = config->sync_mode; } pd_config.config.pt_internal_period = 1000000000UL / config->pt_internal_period; vtysh_config_save(); diff --git a/app/lib/a_process/pd_dbg.c b/app/lib/a_process/pd_dbg.c index 13da583..9c3c3b2 100755 --- a/app/lib/a_process/pd_dbg.c +++ b/app/lib/a_process/pd_dbg.c @@ -382,6 +382,20 @@ int32_t _debug_pkt_status_get(char *buf, int len) return E_NONE; } +/* 获取运行状态报文处理. */ +int32_t _debug_pkt_set_wave_address(char *buf, int len) +{ + dbg_wave_addr_t *wave = (dbg_wave_addr_t*)(buf + sizeof(dbg_pkt_head_t)); + + bzero(&csg.wave_svr, sizeof(csg.wave_svr)); + csg.wave_svr.sin_family = AF_INET; + csg.wave_svr.sin_addr.s_addr = inet_addr(wave->ipv4); + csg.wave_svr.sin_port = htons(wave->port); + _debug_pkt_common_send(buf, DEBUG_SET_WAVE_ADDRESS, 0); + + return E_NONE; +} + int32_t _debug_pkt_upgrade(char *pkt, int len) { static int fd = -1; @@ -534,6 +548,9 @@ int32_t _debug_pkt_process(char *buf, int32_t len) case DEBUG_UPGRADE_DATA: _debug_pkt_upgrade(buf, len); break; + case DEBUG_SET_WAVE_ADDRESS: + _debug_pkt_set_wave_address( buf, len); + break; default: DBG(DBG_M_DEBUG, "Debug not support cmd:%x\n", head->cmd); break; diff --git a/device-tool/config.py b/device-tool/config.py index 0ba37a0..c5488fb 100755 --- a/device-tool/config.py +++ b/device-tool/config.py @@ -18,4 +18,5 @@ class Cmd: UPGRADE_DATA = 0x07 STATE_GET = 0x08 # 获取状态命令 TIME_SYNC = 0x09 # 对时命令 - REBOOT_DEVICE = 0x0A # 重启命令 \ No newline at end of file + REBOOT_DEVICE = 0x0A # 重启命令 + SET_WAVE_ADDRESS = 0x0B # 设置波形接收地址 \ No newline at end of file diff --git a/device-tool/main.py b/device-tool/main.py index b41d588..0d88a3b 100755 --- a/device-tool/main.py +++ b/device-tool/main.py @@ -7,6 +7,7 @@ from pages.global_params import GlobalParamsPage from pages.port_params import PortParamsPage from pages.upgrade import UpgradePage from pages.status_page import StatusPage +from pages.waveform_page import WaveformPage from tcp_client import TCPClient @@ -61,6 +62,9 @@ class DeviceTool: self.status_page = StatusPage(self.notebook, self.tcp_client) self.notebook.add(self.status_page, text="实时状态") + self.waveform_page = WaveformPage(self.notebook, self.tcp_client) + self.notebook.add(self.waveform_page, text="实时波形") + # 可以继续添加其他页面... def toggle_connection(self): diff --git a/device-tool/pages/waveform_page.py b/device-tool/pages/waveform_page.py new file mode 100755 index 0000000..cec51d7 --- /dev/null +++ b/device-tool/pages/waveform_page.py @@ -0,0 +1,369 @@ +import tkinter as tk +from tkinter import ttk +import socket +import threading +import struct +import numpy as np +from matplotlib.figure import Figure +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg +import matplotlib.pyplot as plt + +from protocol import DbgGlobalConfig, DebugWaveAddress +from .base_page import BasePage +from config import Cmd + + +class WaveformPage(BasePage): + def __init__(self, parent, tcp_client): + # 先初始化 WaveformPage 特有的属性 + self.udp_socket = None + self.udp_thread = None + self.udp_running = False + self.udp_port = 10100 + + # 波形数据 + self.waveform_data = {} + self.latest_timestamps = {} + + # 显示配置 + self.sample_points = 10000 + self.sample_duration = 2.0 + self.time_axis = np.linspace(0, self.sample_duration, self.sample_points) + + # 纵轴配置 + self.y_min = -30 + self.y_max = 60 + self.y_precision = 10 + + # 通道配置 + self.channels = list(range(1, 9)) + self.channel_vars = {} + self.channel_colors = plt.cm.Set1(np.linspace(0, 1, 8)) + + # 现在调用父类构造函数 + super().__init__(parent, tcp_client, "实时波形") + + tcp_client.register_callback(Cmd.SET_WAVE_ADDRESS, self.on_set_response) + + def create_widgets(self): + """创建界面控件""" + # 主框架 + main_frame = ttk.Frame(self) + main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + # 控制面板 + control_frame = ttk.LabelFrame(main_frame, text="控制面板", padding=10) + control_frame.pack(fill=tk.X, pady=5) + + # 通道选择 + channel_frame = ttk.Frame(control_frame) + channel_frame.pack(fill=tk.X, pady=5) + + ttk.Label(channel_frame, text="通道选择:").pack(side=tk.LEFT) + + # 创建8个通道的复选框 + for i, channel in enumerate(self.channels): + var = tk.BooleanVar(value=True) + self.channel_vars[channel] = var + + check = ttk.Checkbutton(channel_frame, text=f"通道{channel}", + variable=var, command=self.update_plot) + check.pack(side=tk.LEFT, padx=5) + + # 控制按钮 + button_frame = ttk.Frame(control_frame) + button_frame.pack(fill=tk.X, pady=5) + + ttk.Button(button_frame, text="开始接收", command=self.set_data, width=12).pack(side=tk.LEFT, padx=2) + # ttk.Button(button_frame, text="开始接收", command=self.start_udp, width=12).pack(side=tk.LEFT, padx=2) + ttk.Button(button_frame, text="停止接收", command=self.stop_udp, width=12).pack(side=tk.LEFT, padx=2) + ttk.Button(button_frame, text="清空数据", command=self.clear_data, width=12).pack(side=tk.LEFT, padx=2) + ttk.Button(button_frame, text="自动缩放", command=self.auto_scale, width=12).pack(side=tk.LEFT, padx=2) + + # 状态显示 + self.status_var = tk.StringVar(value="就绪") + ttk.Label(control_frame, textvariable=self.status_var).pack(side=tk.RIGHT) + + # 波形显示区域 + plot_frame = ttk.Frame(main_frame) + plot_frame.pack(fill=tk.BOTH, expand=True, pady=5) + + # 创建 matplotlib 图形 + self.setup_plot(plot_frame) + self.setup_udp() + + def setup_plot(self, parent): + """设置波形图""" + # 创建图形和坐标轴 + self.fig = Figure(figsize=(10, 6), dpi=100) + self.ax = self.fig.add_subplot(111) + + # 设置坐标轴标签 + self.ax.set_xlabel('Time (us)') + self.ax.set_ylabel('Amplitude(mV)') + self.ax.set_title('Real-time Waveform Display') + + # 设置坐标轴范围 + self.ax.set_xlim(0, self.sample_duration) + self.ax.set_ylim(self.y_min, self.y_max) + + # 设置坐标轴刻度 + # 主刻度:每0.1us显示数字标签 + x_major_ticks = np.arange(0, self.sample_duration + 0.1, 0.1) + # 次刻度:每0.05us,只显示网格线不显示数字 + x_minor_ticks = np.arange(0, self.sample_duration + 0.05, 0.05) + + # 纵轴刻度 + y_major_ticks = np.arange(self.y_min, self.y_max + 1, self.y_precision) # 每10mV主刻度 + y_minor_ticks = np.arange(self.y_min, self.y_max + 1, 2) # 每2mV次刻度 + + self.ax.set_xticks(x_major_ticks) + self.ax.set_xticks(x_minor_ticks, minor=True) + self.ax.set_yticks(y_major_ticks) + self.ax.set_yticks(y_minor_ticks, minor=True) + + # 设置网格线 + # 主网格线:0.1us间隔,颜色较深 + self.ax.grid(True, which='major', alpha=0.4, linestyle='-', linewidth=0.8) + # 次网格线:0.05us间隔和2mV间隔,颜色较浅 + self.ax.grid(True, which='minor', alpha=0.2, linestyle='--', linewidth=0.5) + + # 创建画布 + self.canvas = FigureCanvasTkAgg(self.fig, parent) + self.canvas.draw() + self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) + + # 初始化线条对象 + self.lines = {} + for channel in self.channels: + line, = self.ax.plot([], [], + color=self.channel_colors[channel - 1], + linewidth=1, + label=f'通道{channel}') + self.lines[channel] = line + + # 添加图例 + self.ax.legend(loc='upper right') + + def setup_udp(self): + """设置UDP socket""" + try: + self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024 * 1024) # 1MB缓冲区 + self.udp_socket.bind(('0.0.0.0', self.udp_port)) + self.udp_socket.settimeout(1.0) # 设置超时以便可以检查停止标志 + self.status_var.set(f"UDP监听端口: {self.udp_port}") + except Exception as e: + self.status_var.set(f"UDP设置失败: {e}") + + def set_data(self): + """设置波形接收地址""" + if not self.tcp_client.connected: + self.show_error("未连接设备") + return + + try: + addr = DebugWaveAddress() + + addr.wave_ipv4 = bytes(self.get_local_ip(), encoding='utf-8') + addr.wave_port = 10100 + + print(f"wave_ipv4: {addr.wave_ipv4} wave_port:{addr.wave_port}") + + print(f"发送命令: {Cmd.SET_WAVE_ADDRESS}") + if not self.tcp_client.connected: + self.show_error("未连接设备") + return + + self.tcp_client.send_packet(Cmd.SET_WAVE_ADDRESS, addr.to_bytes()) + + except ValueError as e: + self.show_error(f"参数格式错误: {e}") + except Exception as e: + self.show_error(f"设置失败: {e}") + + def start_udp(self): + """开始接收UDP数据""" + + if self.udp_running: + return + + self.udp_running = True + self.udp_thread = threading.Thread(target=self.udp_receive_loop, daemon=True) + self.udp_thread.start() + self.status_var.set("正在接收波形数据...") + + def stop_udp(self): + """停止接收UDP数据""" + self.udp_running = False + if self.udp_thread and self.udp_thread.is_alive(): + self.udp_thread.join(timeout=2.0) + self.status_var.set("已停止接收") + + def udp_receive_loop(self): + """UDP数据接收循环""" + buffer = b'' + + while self.udp_running: + try: + data, addr = self.udp_socket.recvfrom(65536) # 最大接收64KB + print(f"接收到来自 {addr} 的数据:") + print(f"原始数据: {data}") + print(f"数据长度: {len(data)} 字节") + buffer += data + + # 数据头大小(前32字节) + data_header_size = 32 + + # 波形头部格式(csg_real_image_t 结构体) + wave_header_format = '= total_header_size: + # 1. 提取数据头(前32字节) + data_header = buffer[:data_header_size] + print(f"数据头: {data_header.hex()}") + + # 2. 提取波形头部(接下来的wave_header_size字节) + wave_header_start = data_header_size + wave_header_end = data_header_size + wave_header_size + wave_header_data = buffer[wave_header_start:wave_header_end] + + # 3. 解析波形头部 + ( + channel_id, reserved1, channel_type, unit, + sample_rate, record_points, phase, pulse_peak, + pre_trigger, trigger_level, filter_frequency, + rise_time, peak_time, fall_time, pulse_width, peak_count, + reserved2, signal_envelope_area, signal_mean, signal_variance, + first_main_freq, first_main_freq_peak, spectral_peak_count, + spectral_mean, spectral_variance, century_second, nanosecond, + cycle_count, frequency, noise, sampling_time, reserved3 + ) = struct.unpack(wave_header_format, wave_header_data) + + # 4. 计算波形数据大小 + waveform_size = record_points * 2 # 每个点2字节 + total_packet_size = total_header_size + waveform_size + + if len(buffer) < total_packet_size: + break # 数据不完整,等待更多数据 + + # 5. 提取波形数据(最后的部分) + waveform_start = total_header_size + waveform_end = total_header_size + waveform_size + waveform_bytes = buffer[waveform_start:waveform_end] + + # 6. 解析波形数据 (int16数组) + waveform_data = np.frombuffer(waveform_bytes, dtype=np.int16) + + # 打印前100个原始值 + print("前100个原始波形数据值:") + for i in range(min(100, len(waveform_data))): + print(f"索引 {i:3d}: {waveform_data[i]:6d}") + + # 7. 直接使用接收到的mV值,转换为float32以便matplotlib处理 + waveform_mv = waveform_data.astype(np.float32) + + # 8. 限制量程范围(如果超过量程,显示最大量程) + # 根据当前的纵轴配置 self.y_min 和 self.y_max + waveform_mv = np.clip(waveform_mv, self.y_min, self.y_max) + + # 9. 更新数据 + self.waveform_data[channel_id] = waveform_mv + self.latest_timestamps[channel_id] = (century_second, nanosecond) + + # 10. 打印调试信息 + print(f"通道{channel_id}: {record_points}个采样点, 采样率{sample_rate}Msps") + print(f"时间: {century_second}.{nanosecond:09d}") + print(f"脉冲峰值: {pulse_peak}mV, 频率: {frequency}Hz") + print(f"波形数据点数: {len(waveform_data)}") + print(f"数据范围: {np.min(waveform_data)} ~ {np.max(waveform_data)} mV") + + # 11. 更新显示 + self.update_plot() + + # 12. 从缓冲区移除已处理的数据 + buffer = buffer[total_packet_size:] + + except socket.timeout: + continue # 超时是正常的,继续循环 + except Exception as e: + print(f"UDP接收错误: {e}") + import traceback + traceback.print_exc() + continue + + def update_plot(self): + """更新波形显示""" + if not hasattr(self, 'ax'): + return + + # 清除之前的线条数据 + for channel in self.channels: + self.lines[channel].set_data([], []) + + # 更新选中的通道数据 + has_data = False + for channel in self.channels: + if (self.channel_vars[channel].get() and + channel in self.waveform_data and + len(self.waveform_data[channel]) == self.sample_points): + waveform = self.waveform_data[channel] + self.lines[channel].set_data(self.time_axis, waveform) + has_data = True + + # 如果有数据显示,更新图形 + if has_data: + self.ax.relim() + self.ax.autoscale_view() + self.canvas.draw_idle() + + def auto_scale(self): + """自动缩放坐标轴""" + if hasattr(self, 'ax'): + self.ax.set_xlim(0, self.sample_duration) + self.ax.set_ylim(self.y_min, self.y_max) + self.canvas.draw_idle() + + def clear_data(self): + """清空波形数据""" + self.waveform_data.clear() + self.latest_timestamps.clear() + self.update_plot() + self.status_var.set("数据已清空") + + def get_channel_info(self, channel_id): + """获取通道信息字符串""" + if channel_id in self.latest_timestamps: + century_second, nanosecond = self.latest_timestamps[channel_id] + return f"通道{channel_id} - 时间: {century_second}.{nanosecond:09d}" + return f"通道{channel_id} - 无数据" + + def on_close(self): + """页面关闭时的清理工作""" + self.stop_udp() + if self.udp_socket: + self.udp_socket.close() + + def on_set_response(self, header, body): + """处理设置响应""" + self.show_info("波形地址设置成功") + self.start_udp() + + def ip_to_int(self, ip): + parts = ip.split('.') + int_ip = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3]) + return int_ip + + def ipv4_to_int(self, ipv4_address): + + packed_ip = socket.inet_aton(ipv4_address) + + return struct.unpack("!L", packed_ip)[0] + + def get_local_ip(self): + ip = socket.gethostbyname(socket.gethostname()) + return ip \ No newline at end of file diff --git a/device-tool/protocol.py b/device-tool/protocol.py index 07eab5a..9bbafac 100755 --- a/device-tool/protocol.py +++ b/device-tool/protocol.py @@ -224,6 +224,25 @@ class DebugPktState: raise ValueError("Data too short for state data") return cls(*struct.unpack(cls.FORMAT, data[:cls.SIZE])) +@dataclass +class DebugWaveAddress: + wave_ipv4: bytes = bytes(16) + wave_port: int = 0 + + FORMAT = '<16s H' + SIZE = struct.calcsize(FORMAT) + + def to_bytes(self): + return struct.pack( + self.FORMAT, + self.wave_ipv4, self.wave_port) + + @classmethod + def from_bytes(cls, data): + if len(data) < cls.SIZE: + raise ValueError("Data too short for wave address data") + return cls(*struct.unpack(cls.FORMAT, data[:cls.SIZE])) + # 工具函数 def int_to_ip(ip_int): return f"{ip_int & 0xFF}.{(ip_int >> 8) & 0xFF}.{(ip_int >> 16) & 0xFF}.{(ip_int >> 24) & 0xFF}" @@ -238,6 +257,49 @@ def bytes_to_mac(mac_bytes): return ':'.join(f'{b:02x}' for b in mac_bytes) +@dataclass +class CsgRealImage: + channel_id: int = 0 # 通道编号 + reserved1: int = 0 # 保留 + channel_type: int = 0 # 通道类型 (1: UHF) + unit: int = 0 # 单位 (1: dBm; 2: mV) + sample_rate: int = 0 # 采样率 (Msps) + record_points: int = 0 # 采样点数 + century_second: int = 0 # 采样时间世纪秒 + nanosecond: int = 0 # 纳秒 + cycle_count: int = 0 # 周期数 + frequency: int = 0 # 频率 + noise: int = 0 # 噪声 + sampling_time: int = 0 # 采样时长 + reserved3: bytes = bytes(20) # 预留 + + FORMAT = '