_FYAW智能显示控制仪表的简单使用_串口通信
一、简介
该仪表可以实时显示位移传感器的测量值,并可设定阈值等。先谈谈简单的使用方法,通过说明书,我们可以知道长按SET键可以进入参数选择状态,按“↑”“↓”可以选择该组参数的上一个或者下一个参数。
从参数一览中可以看到有不同组的参数,当我们第一次进入参数选择状态时会进入第一组参数,可以设置不同的阈值。只不过由于是数码管,显示字母时会用一些比较奇怪的表达,比如“5”其实就是“S”,可以通过对照参数表,获取不同字母的显示。
如果想进入其他组参数,可以在第一组参数中,通过“↑”或“↓”找到最后一个oA,然后按“←”开始设置参数,当把4位数码管都设为1,即输入密码1111后,再按下SET确定,就可以解锁密码了。此时可以通过长按SET键切换到其他组参数。
更多功能可自行查看数据手册,不过要注意的是说明书中的参数并非全部与实际仪器一一对应,实际仪器有时会缺少一两个参数。
二、串口使用
串口使用的是RS485电平或者RS232,千万要注意的就是A、B的接线,不要接错。这个一般需要按照说明书的来,说明书上说“7”对应的是“B”,“8”对应的是“A”,如果仪器上贴着的标签是相反的,那么可以先按照说明书上的接法,如果不行再按照仪器上的,不要忘记接地。
接下来说说具体的串口通信,仪器默认波特率是9600,通信协议是Modbus-RTU。这里推荐使用Modbus-RTU协议。
这个通信过程并非是仪表主动不断地发送测量数据给上位机,而是需要你先发送相应命令给仪器,然后接收仪器数据。使用过程中倒是发现一些与说明书不同吻合的地方,比如读取测量值这一步,按理来说应答应如下,但实际过程中接收的是“01 04 04 42 47 3F 3F 3F 28”,即多了一个04
不过当我们需要连续读取时就会发现单次发送实在是麻烦,现在可以有下面几种方法连续发下
1,使用llcom
这个串口助手可以写lua脚本,以实现自动发送数据,并读取数据保存
chenxuuu/llcom: 🛠功能强大的串口工具。支持Lua自动化处理、串口调试、WinUSB、串口曲线、TCP测试、MQTT测试、编码转换、乱码恢复等功能 (github.com)
下面是可以发送命令并把读取数据保存起来的lua脚本。只不过lua脚本很难运行什么GUI,自然就无法显示图表
-- 发送数据中间间隔时间(单位ms) local sendDelay = 100-- 生成16进制数据“01 04 0000 0002 71CB” local usartData = "01040000000271CB" usartData=usartData:fromHex()-- 获取当前日期和时间 local function get_current_datetime()local datetime = os.date("%Y%m%d_%H%M%S")return datetime end-- 生成带有日期和时间的文件名 local function generate_filename()local datetime = get_current_datetime()return "D:/Script/python/expr_com/data_log/log_" .. datetime .. ".txt" end-- 打开文件,如果文件不存在则创建,如果存在则覆盖 local filePath = generate_filename() local file, err = io.open(filePath, "w") if not file then-- 如果文件打开失败,输出错误信息print("无法打开文件: " .. err) elseprint("文件成功打开: " .. filePath) endlocal value_str = ""-- 发送数据的函数 apiSetCb("uart", function(data)-- 写入数据value_str = data:toHex():sub(7, 14)file:write(value_str .. "\n") -- 添加换行符以便区分不同数据print(value_str) end)-- 循环发送任务 sys.taskInit(function()while true do-- 发送数据apiSendUartData(usartData)sys.wait(sendDelay)end end)-- 确保在脚本结束时关闭文件 --[[ atexit(function()if file thenfile:close()print("文件已关闭")end end) ]]
2,使用Python脚本
使用Python脚本直接打开串口,然后发送命令并读取数据,需要注意的是下面脚本里指定了一个串口,你需要打开设备管理器来找到实际串口并修改脚本里的串口为实际串口号。同时注意波特率设置。
由于仪器发送的其实是浮点数据的实际表达,所以下面脚本就自动做了这个转换
可惜的是,使用Python后,无法很好地实时更新数据到图表中,下面也就没有添加这个功能。
import serial import struct import time import os from datetime import datetime# 配置串口 ser = serial.Serial(port='COM7', # 根据实际情况修改端口号baudrate=115200, # 波特率timeout=1 # 超时设置 )# 要发送的16进制数据 send_data = bytes.fromhex('01040000000271CB')# 确保 data_log 目录存在 log_dir = 'data_log' os.makedirs(log_dir, exist_ok=True)# 获取当前日期和时间 current_time = datetime.now().strftime('%Y%m%d_%H%M%S') log_file_path = os.path.join(log_dir, f"log_{current_time}.csv")# 打开日志文件 with open(log_file_path, 'w') as log_file:log_file.write("Timestamp,Value\n") # 写入表头try:while True:# 发送16进制数据ser.write(send_data)# 从串口读取9字节的数据,并提取中间的5-8位16进制数据data = ser.read(9)[3:7]# 将16进制数据转换为32位浮点数try:float_data = struct.unpack('>f', data)[0] # '>f' 表示大端模式print(f"data: {float_data}") # 打印浮点数# 获取当前时间戳timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')# 写入日志文件log_file.write(f"{timestamp},{float_data}\n")log_file.flush() # 立即写入文件except struct.error as e:print(f"Error unpacking data: {e}")# 每100毫秒(即每秒10次)更新一次time.sleep(0.1)except KeyboardInterrupt:print("Program terminated by user")finally:# 关闭串口ser.close()
CRC校验计算
补充一点,为了可以使用其他命令,需要计算16位CRC校验值。比如下面可以数据手册上的读取测量值命令的CRC校验值“01 04 0000 0002 71CB”,其中71CB是16位校验值,在下面脚本中输入前面命令“01 04 0000 0002”即可
import crcmoddef calculate_crc16(data):# 创建一个CRC16校验对象crc16_func = crcmod.mkCrcFun(0x18005, initCrc=0xFFFF, rev=True, xorOut=0x0000)# 将16进制字符串转换为字节byte_data = bytes.fromhex(data)# 计算CRC16校验码crc_value = crc16_func(byte_data)# 交换高低字节crc_value = ((crc_value & 0xFF) << 8) | ((crc_value & 0xFF00) >> 8)return crc_value# 输入的16进制内容 hex_data = "01 04 0000 0002"# 去除空格并计算CRC16 hex_data_no_spaces = hex_data.replace(" ", "") crc16_result = calculate_crc16(hex_data_no_spaces)# 打印结果 print(f"CRC16:{crc16_result:04X}")
8位16进制字符串转为32位浮点数据
def hex_to_float(hex_str):# 将 16 进制字符串转换为 32 位无符号整数uint_value = int(hex_str, 16)# 将 32 位无符号整数转换为浮点数float_value = struct.unpack('!f', struct.pack('!I', uint_value))[0]return float_value
3,使用WPF
就功能而言,这个方法我是最满意的,可以自己定制化写一个专用的串口助手。不过它们各有缺陷,llcom虽然可以使用lua脚本做很多自动化处理,但无法显示图表。python虽然可以显示图表,但实时更新的效果并不好。使用WPF,虽然可以实现很多功能,但需要搭建Visual Studio环境,并且需要写不少代码。
由于时间限制,目前我只实现了简单的定时发送,转为浮点数据的功能。后续,我会添加显示图表的功能,记录日志的功能还没有做好,使用会出问题。并且我暂时并不打算在这上面花太多时间,所以没怎么考虑界面设计。
因此,界面很简单,使用起来也很简单。
XAML文件
<Window x:Class="expr_com.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="expr_com" Height="700" Width="1000"><Grid><Grid.RowDefinitions><RowDefinition Height="Auto" /><!-- 可用串口 --><RowDefinition Height="Auto" /><!-- 波特率 --><RowDefinition Height="Auto" /><!-- 打开串口按钮 --><RowDefinition Height="Auto" /><!-- 发送数据 --><RowDefinition Height="Auto" /><!-- 16进制复选框 --><RowDefinition Height="Auto" /><!-- 16进制显示复选框 --><RowDefinition Height="Auto" /><!-- 单次发送和定时循环发送按钮 --><RowDefinition Height="Auto" /><!-- 周期(秒) --><RowDefinition Height="Auto" /><!-- 记录数据复选框 --><RowDefinition Height="Auto" /><!-- 处理数据复选框及参数 --><RowDefinition Height="*" /><!-- 接收数据框 --><RowDefinition Height="*" /><!-- 处理数据框 --></Grid.RowDefinitions><StackPanel Orientation="Horizontal" Grid.Row="0" Margin="10,10,10,0"><Label Content="可用串口:" Width="80" /><ComboBox Name="cmbPorts" SelectionChanged="cmbPorts_SelectionChanged" Width="150" /></StackPanel><StackPanel Orientation="Horizontal" Grid.Row="1" Margin="10,0,10,0"><Label Content="波特率:" Width="80" /><TextBox Name="txtBaudRate" Text="115200" Width="100" /></StackPanel><Button Name="btnOpenPort" Content="打开串口" Click="btnOpenPort_Click" Width="120" Margin="10,0,10,10" Grid.Row="2" /><StackPanel Orientation="Horizontal" Grid.Row="3" Margin="10,0,10,0"><Label Content="发送数据:" Width="80" /><TextBox Name="txtSendData" Text="01040000000271CB" Width="200" /></StackPanel><CheckBox Name="chkHex" Content="16进制" Margin="10,0,10,10" Grid.Row="4" /><CheckBox Name="chkHexDisplay" Content="16进制显示" Margin="10,0,10,10" Grid.Row="5" /><StackPanel Orientation="Horizontal" Grid.Row="6" Margin="10,0,10,0"><Button Name="btnSendOnce" Content="单次发送" Click="btnSendOnce_Click" Width="120" /><Button Name="btnStartLoopSend" Content="定时循环发送" Click="btnStartLoopSend_Click" Width="120" Margin="10,0,0,0" /></StackPanel><StackPanel Orientation="Horizontal" Grid.Row="7" Margin="10,0,10,0"><Label Content="周期(秒):" Width="80" /><TextBox Name="txtPeriod" Text="0.1" Width="50" /></StackPanel><CheckBox Name="chkLog" Content="记录数据" Margin="10,0,10,10" Grid.Row="8" /><StackPanel Orientation="Horizontal" Grid.Row="9" Margin="10,0,10,0"><CheckBox Name="chkProcessData" Content="处理数据" Margin="0,0,10,0" /><Label Content="起始位置:" Width="80" /><TextBox Name="txtStartIndex" Text="7" Width="50" Margin="0,0,10,0" /><Label Content="长度:" Width="50" /><TextBox Name="txtLength" Text="8" Width="50" /></StackPanel><TextBox Name="txtReceivedData" IsReadOnly="True" Margin="10,0,10,10" Grid.Row="10" VerticalScrollBarVisibility="Auto" /><Grid Grid.Row="11" Margin="10,0,10,10"><Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><TextBox Name="txtExtractedData" IsReadOnly="True" VerticalScrollBarVisibility="Auto" Grid.Column="0" /><TextBox Name="txtFloatData" IsReadOnly="True" VerticalScrollBarVisibility="Auto" Grid.Column="1" /></Grid></Grid> </Window>
CS代码
using System; using System.IO.Ports; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.IO; using System.Timers;namespace expr_com {public partial class MainWindow : Window{private SerialPort? _serialPort; // 声明为可为 null 的类型private System.Timers.Timer? _timer; // 声告为可为 null 的类型public MainWindow(){InitializeComponent();LoadAvailablePorts();_serialPort = null; // 初始化为 null_timer = null; // 初始化为 null}private void LoadAvailablePorts(){cmbPorts.ItemsSource = SerialPort.GetPortNames();}private void cmbPorts_SelectionChanged(object sender, SelectionChangedEventArgs e){if (cmbPorts.SelectedItem != null){txtBaudRate.Focus();}}private void btnOpenPort_Click(object sender, RoutedEventArgs e){if (cmbPorts.SelectedItem == null){MessageBox.Show("请选择一个串口。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);return;}if (_serialPort == null || !_serialPort.IsOpen){try{_serialPort = new SerialPort(cmbPorts.SelectedItem.ToString(), int.Parse(txtBaudRate.Text)){ReadTimeout = 1000, // 增加读取超时时间WriteTimeout = 500,DataBits = 8,StopBits = StopBits.One,Parity = Parity.None};// 注册 DataReceived 事件_serialPort.DataReceived += SerialPort_DataReceived;_serialPort.Open();btnOpenPort.Content = "关闭串口";// 显示成功消息txtReceivedData.AppendText("← 串口已打开。" + Environment.NewLine);}catch (Exception ex){MessageBox.Show($"打开串口失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);txtReceivedData.AppendText($"← 打开串口失败: {ex.Message}" + Environment.NewLine);}}else{_serialPort.DataReceived -= SerialPort_DataReceived; // 取消注册 DataReceived 事件_serialPort.Close();btnOpenPort.Content = "打开串口";// 显示成功消息txtReceivedData.AppendText("← 串口已关闭。" + Environment.NewLine);}}private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e){try{string data = _serialPort.ReadExisting();if (!string.IsNullOrEmpty(data)){Dispatcher.Invoke(() =>{if (chkHexDisplay.IsChecked == true){// 将接收到的数据转换为16进制字符串byte[] bytes = Encoding.ASCII.GetBytes(data);string hexData = BitConverter.ToString(bytes).Replace("-", " ");txtReceivedData.AppendText($"→ {hexData}" + Environment.NewLine);if (chkProcessData.IsChecked == true){ProcessData(hexData);}}else{txtReceivedData.AppendText($"→ {data}" + Environment.NewLine);}// 立即滚动到底部txtReceivedData.ScrollToEnd();});}}catch (Exception ex){// 处理其他异常Dispatcher.Invoke(() =>{txtReceivedData.AppendText($"→ 读取错误: {ex.Message}" + Environment.NewLine);});}}private void LogData(string data){string logFilePath = Path.Combine("data_log", $"log_{DateTime.Now:yyyyMMdd_HHmmss}.csv");File.AppendAllText(logFilePath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss},{data}{Environment.NewLine}");}private async void btnSendOnce_Click(object sender, RoutedEventArgs e){await SendDataAsync(false);}private async void btnStartLoopSend_Click(object sender, RoutedEventArgs e){if (_timer == null || !_timer.Enabled){try{double period = double.Parse(txtPeriod.Text);_timer = new System.Timers.Timer(period * 1000); // 转换为毫秒_timer.Elapsed += OnTimedEvent;_timer.AutoReset = true;_timer.Enabled = true;btnStartLoopSend.Content = "停止循环发送";// 显示启动消息txtReceivedData.AppendText($"← 定时循环发送已启动,周期: {period} 秒。" + Environment.NewLine);}catch (Exception ex){MessageBox.Show($"设置定时发送失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);txtReceivedData.AppendText($"← 设置定时发送失败: {ex.Message}" + Environment.NewLine);}}else{_timer.Enabled = false;_timer.Dispose();_timer = null;btnStartLoopSend.Content = "定时循环发送";// 显示停止消息txtReceivedData.AppendText("← 定时循环发送已停止。" + Environment.NewLine);}}private async void OnTimedEvent(object source, ElapsedEventArgs e){try{await Dispatcher.InvokeAsync(async () =>{await SendDataAsync(true);});}catch (Exception ex){// 处理其他异常await Dispatcher.InvokeAsync(() =>{txtReceivedData.AppendText($"← 发送错误: {ex.Message}" + Environment.NewLine);});}}private async Task SendDataAsync(bool isLoop){try{if (_serialPort != null && _serialPort.IsOpen){string sendData = txtSendData.Text.Trim();if (string.IsNullOrEmpty(sendData)){throw new InvalidOperationException("发送数据不能为空。");}if (chkHex.IsChecked == true){byte[] hexData = Convert.FromHexString(sendData);_serialPort.Write(hexData, 0, hexData.Length);}else{_serialPort.WriteLine(sendData);}// 显示发送数据txtReceivedData.AppendText($"← {sendData}" + Environment.NewLine);if (chkLog.IsChecked == true){LogData(sendData);}}else{// 串口未打开,显示错误信息txtReceivedData.AppendText("← 串口未打开,无法发送数据。" + Environment.NewLine);}}catch (Exception ex){// 处理其他异常txtReceivedData.AppendText($"← 发送错误: {ex.Message}" + Environment.NewLine);}}private void ProcessData(string hexData){try{// 从文本框中获取用户输入的起始位置和长度int startIndex = int.Parse(txtStartIndex.Text) - 1; // 减1是为了适应索引从0开始int length = int.Parse(txtLength.Text);// 去除空格string hexDataWithoutSpaces = hexData.Replace(" ", "");// 检查数据长度是否足够if (hexDataWithoutSpaces.Length >= startIndex + length){// 截取指定位置和长度的数据string hexDataToProcess = hexDataWithoutSpaces.Substring(startIndex, length);// 将16进制字符串转换为字节数组byte[] bytes = Convert.FromHexString(hexDataToProcess);// 检查字节顺序if (BitConverter.IsLittleEndian){// 如果系统是小端序,而数据是大端序,则需要反转字节顺序Array.Reverse(bytes);}// 将字节数组转换为浮点数float floatValue = BitConverter.ToSingle(bytes, 0);// 追加结果txtExtractedData.AppendText($"{hexDataToProcess}" + Environment.NewLine);txtFloatData.AppendText($"{floatValue}" + Environment.NewLine);// 立即滚动到底部txtExtractedData.ScrollToEnd();txtFloatData.ScrollToEnd();}else{txtExtractedData.AppendText("数据长度不足,无法处理。" + Environment.NewLine);}}catch (FormatException ex){txtExtractedData.AppendText($"格式错误: {ex.Message}" + Environment.NewLine);}}} }
实际效果:
单次发送
循环发送
2024.11.21
添加了 LiveCharts组件,可以实现实时更新,并且图表的最大显示点数可以随时设置。同时添加了是否禁用动画选项,启用该选项可以显著提高流畅度。注意注意的是,这个图表的数据来源仅仅绑定了转换后的浮点数据,而非接收的原数据。
此时,单个文件的代码似乎有些挤了。
为了方便观察效果,创建了一个调试按钮,以100Hz的速度更新数据,禁用动画后,图像可以很流畅的滚动更新。
XAML:
<Window x:Class="expr_com.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"Title="MainWindow" Height="800" Width="1200"><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="*" /></Grid.ColumnDefinitions><Grid Grid.Column="0"><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="*" /><RowDefinition Height="*" /></Grid.RowDefinitions><StackPanel Orientation="Horizontal" Grid.Row="0" Margin="10,10,10,0"><Label Content="可用串口:" Width="80" /><ComboBox Name="cmbPorts" SelectionChanged="cmbPorts_SelectionChanged" Width="106" Height="19" /></StackPanel><StackPanel Orientation="Horizontal" Grid.Row="1" Margin="10,0,10,0"><Label Content="波特率:" Width="80" /><TextBox Name="txtBaudRate" Text="115200" Width="100" Height="18" /></StackPanel><Button Name="btnOpenPort" Content="打开串口" Click="btnOpenPort_Click" Width="120" Margin="10,0,10,10" Grid.Row="2" /><StackPanel Orientation="Horizontal" Grid.Row="3" Margin="10,0,10,0"><Label Content="发送数据:" Width="80" /><TextBox Name="txtSendData" Text="01040000000271CB" Width="200" Height="18" /></StackPanel><CheckBox Name="chkHex" Content="16进制" Margin="10,0,10,10" Grid.Row="4" /><CheckBox Name="chkHexDisplay" Content="16进制显示" Margin="10,0,10,10" Grid.Row="5" /><StackPanel Orientation="Horizontal" Grid.Row="6" Margin="10,0,10,0"><Button Name="btnSendOnce" Content="单次发送" Click="btnSendOnce_Click" Width="112" /><Button Name="btnStartLoopSend" Content="定时循环发送" Click="btnStartLoopSend_Click" Width="105" Margin="10,0,0,0" RenderTransformOrigin="1.418,0.603" /></StackPanel><StackPanel Orientation="Horizontal" Grid.Row="7" Margin="10,0,10,0"><Label Content="周期(秒):" Width="80" /><TextBox Name="txtPeriod" Text="0.1" Width="50" Height="18" /></StackPanel><CheckBox Name="chkLog" Content="记录数据" Margin="10,0,10,264" Grid.Row="8" Grid.RowSpan="3" /><TextBox Name="txtReceivedData" IsReadOnly="True" Margin="10,45,10,52" Grid.Row="9" VerticalScrollBarVisibility="Auto" Grid.RowSpan="2" /><StackPanel Orientation="Horizontal" Grid.Row="10" Margin="10,254,10,0" Height="23" VerticalAlignment="Top"><CheckBox x:Name="chkProcessData" Content="处理数据" Height="14" RenderTransformOrigin="0.804,0.788" Width="80" /><Label Content="起始位置:" Width="80" /><TextBox x:Name="txtStartIndex" Text="7" Width="50" Margin="0,0,10,0" Height="18" /><Label Content="长度:" Width="50" /><TextBox x:Name="txtLength" Text="8" Width="50" Height="18" /></StackPanel><Grid Grid.Row="11" Margin="10,0,10,10"><Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="*" /></Grid.ColumnDefinitions><TextBox Name="txtExtractedData" IsReadOnly="True" VerticalScrollBarVisibility="Auto" Grid.Column="0" /><TextBox Name="txtFloatData" IsReadOnly="True" VerticalScrollBarVisibility="Auto" Grid.Column="1" /></Grid></Grid><Grid Grid.Column="1" RenderTransformOrigin="-0.073,0.281"><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="*" /></Grid.RowDefinitions><CheckBox Name="chkShowChart" Content="显示图表" Margin="10,10,10,0" Grid.Row="0" Checked="chkShowChart_Checked" Unchecked="chkShowChart_Unchecked" /><StackPanel Orientation="Horizontal" Grid.Row="1" Margin="10,0,10,0"><Label Content="最大显示点数:" Width="100" /><TextBox Name="txtMaxPoints" Text="100" Width="50" Height="18" TextChanged="txtMaxPoints_TextChanged" /><CheckBox Name="chkDisableAnimation" Content="禁用动画" Checked="chkDisableAnimation_Checked" Unchecked="chkDisableAnimation_Unchecked" /><!--<Button Name="btnDebug" Content="测试" Click="btnDebug_Click" Width="120" />--></StackPanel><lvc:CartesianChart Name="chart" Grid.Row="2" Margin="10" Visibility="Collapsed"><lvc:CartesianChart.AxisX><lvc:Axis MinValue="1" MaxValue="100" /></lvc:CartesianChart.AxisX><lvc:CartesianChart.AxisY><lvc:Axis LabelFormatter="{Binding Formatter}"/></lvc:CartesianChart.AxisY><lvc:CartesianChart.Series><lvc:LineSeries Values="{Binding FloatValues}" Title="浮点数据" /></lvc:CartesianChart.Series></lvc:CartesianChart></Grid></Grid> </Window>
C#:
using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO.Ports; using System.Text; using System.Threading.Tasks; using System.Timers; using System.Windows; using System.Windows.Controls; using System.Windows.Interop; using System.Windows.Media; using LiveCharts; using LiveCharts.Definitions.Charts; using LiveCharts.Wpf;namespace expr_com {public partial class MainWindow : Window{private SerialPort? _serialPort;private System.Timers.Timer? _timer;private ObservableCollection<double> _floatValues = new ObservableCollection<double>();private int _maxPoints = 100;// 判断private bool isChartVisible = false;// 定义一个可绑定的数据集合public ChartValues<double> FloatValues { get; set; }// 调试用的// 定义一个静态变量private static float debugCount = 0;private System.Timers.Timer? _debugTimer=null;private Random random = new Random();public MainWindow(){InitializeComponent();LoadAvailablePorts();DataContext = this;_serialPort = null;_timer = null;txtMaxPoints.Text = _maxPoints.ToString();// 初始化数据集合FloatValues = new ChartValues<double>();// 将数据集合绑定到图表chart.Series = new SeriesCollection{new LineSeries{Values = FloatValues,Title = "浮点数据"}};// 初始化图表chart.Visibility = chkShowChart.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;}public event PropertyChangedEventHandler? PropertyChanged;private void OnPropertyChanged(string propertyName){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}// 加载可用端口private void LoadAvailablePorts(){cmbPorts.ItemsSource = SerialPort.GetPortNames();}// 选择串口private void cmbPorts_SelectionChanged(object sender, SelectionChangedEventArgs e){if (cmbPorts.SelectedItem != null){txtBaudRate.Focus();}}// 打开端口private void btnOpenPort_Click(object sender, RoutedEventArgs e){if (cmbPorts.SelectedItem == null){MessageBox.Show("请选择一个串口。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);return;}if (_serialPort == null || !_serialPort.IsOpen){try{_serialPort = new SerialPort(cmbPorts.SelectedItem.ToString(), int.Parse(txtBaudRate.Text)){ReadTimeout = 1000,WriteTimeout = 500,DataBits = 8,StopBits = StopBits.One,Parity = Parity.None};_serialPort.DataReceived += SerialPort_DataReceived;_serialPort.Open();btnOpenPort.Content = "关闭串口";txtReceivedData.AppendText("← 串口已打开。" + Environment.NewLine);}catch (Exception ex){MessageBox.Show($"打开串口失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);txtReceivedData.AppendText($"← 打开串口失败: {ex.Message}" + Environment.NewLine);}}else{_serialPort.DataReceived -= SerialPort_DataReceived;_serialPort.Close();btnOpenPort.Content = "打开串口";txtReceivedData.AppendText("← 串口已关闭。" + Environment.NewLine);}}// 串口数据接收private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e){try{string data = _serialPort.ReadExisting();if (!string.IsNullOrEmpty(data)){Dispatcher.Invoke(() =>{if (chkHexDisplay.IsChecked == true){byte[] bytes = Encoding.ASCII.GetBytes(data);string hexData = BitConverter.ToString(bytes).Replace("-", " ");txtReceivedData.AppendText($"→ {hexData}" + Environment.NewLine);if (chkProcessData.IsChecked == true){ProcessData(hexData);}}else{txtReceivedData.AppendText($"→ {data}" + Environment.NewLine);}txtReceivedData.ScrollToEnd();});}}catch (Exception ex){Dispatcher.Invoke(() =>{txtReceivedData.AppendText($"→ 读取错误: {ex.Message}" + Environment.NewLine);});}}// 日志数据记录private void LogData(string data){string logFilePath = System.IO.Path.Combine("data_log", $"log_{DateTime.Now:yyyyMMdd_HHmmss}.csv");System.IO.File.AppendAllText(logFilePath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss},{data}{Environment.NewLine}");}// 单次发送数据private async void btnSendOnce_Click(object sender, RoutedEventArgs e){await SendDataAsync(false);}// 定时循环发送private async void btnStartLoopSend_Click(object sender, RoutedEventArgs e){if (_timer == null || !_timer.Enabled){try{double period = double.Parse(txtPeriod.Text);_timer = new System.Timers.Timer(period * 1000);_timer.Elapsed += OnTimedEvent;_timer.AutoReset = true;_timer.Enabled = true;btnStartLoopSend.Content = "停止循环发送";txtReceivedData.AppendText($"← 定时循环发送已启动,周期: {period} 秒。" + Environment.NewLine);}catch (Exception ex){MessageBox.Show($"设置定时发送失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);txtReceivedData.AppendText($"← 设置定时发送失败: {ex.Message}" + Environment.NewLine);}}else{_timer.Enabled = false;_timer.Dispose();_timer = null;btnStartLoopSend.Content = "定时循环发送";txtReceivedData.AppendText("← 定时循环发送已停止。" + Environment.NewLine);}}// 定时事件处理private async void OnTimedEvent(object? source, ElapsedEventArgs e){try{await Dispatcher.InvokeAsync(async () =>{await SendDataAsync(true);});}catch (Exception ex){await Dispatcher.InvokeAsync(() =>{txtReceivedData.AppendText($"← 发送错误: {ex.Message}" + Environment.NewLine);});}}// 异步发送数据private async Task SendDataAsync(bool isLoop){try{if (_serialPort != null && _serialPort.IsOpen){string sendData = txtSendData.Text.Trim();if (string.IsNullOrEmpty(sendData)){throw new InvalidOperationException("发送数据不能为空。");}if (chkHex.IsChecked == true){byte[] hexData = Convert.FromHexString(sendData);_serialPort.Write(hexData, 0, hexData.Length);}else{_serialPort.WriteLine(sendData);}txtReceivedData.AppendText($"← {sendData}" + Environment.NewLine);if (chkLog.IsChecked == true){LogData(sendData);}}else{txtReceivedData.AppendText("← 串口未打开,无法发送数据。" + Environment.NewLine);}}catch (Exception ex){txtReceivedData.AppendText($"← 发送错误: {ex.Message}" + Environment.NewLine);}}// 处理数据,把16进制数据转换为浮点数private void ProcessData(string hexData){try{int startIndex = int.Parse(txtStartIndex.Text) - 1;int length = int.Parse(txtLength.Text);string hexDataWithoutSpaces = hexData.Replace(" ", "");if (hexDataWithoutSpaces.Length >= startIndex + length){string hexDataToProcess = hexDataWithoutSpaces.Substring(startIndex, length);byte[] bytes = Convert.FromHexString(hexDataToProcess);if (BitConverter.IsLittleEndian){Array.Reverse(bytes);}float floatValue = BitConverter.ToSingle(bytes, 0);// 提取数据并显示txtExtractedData.AppendText($"{hexDataToProcess}" + Environment.NewLine);txtFloatData.AppendText($"{floatValue}" + Environment.NewLine);txtExtractedData.ScrollToEnd();txtFloatData.ScrollToEnd();// 添加数据到图表if (chkShowChart.IsChecked == true){AddDataPoint(floatValue);}}else{txtExtractedData.AppendText("数据长度不足,无法处理。" + Environment.NewLine);}}catch (FormatException ex){txtExtractedData.AppendText($"格式错误: {ex.Message}" + Environment.NewLine);}}// 添加数据点到画布上private void AddDataPoint(double value){FloatValues.Add(value);// 如果数据点过多,可以移除旧的数据点以保持图表整洁if (FloatValues.Count > int.Parse(txtMaxPoints.Text)){FloatValues.RemoveAt(0);}}// 是否选择显示图表private void chkShowChart_Checked(object sender, RoutedEventArgs e){chart.Visibility = Visibility.Visible;isChartVisible = true;}private void chkShowChart_Unchecked(object sender, RoutedEventArgs e){chart.Visibility = Visibility.Collapsed;isChartVisible = false;}// 最大显示点数private void txtMaxPoints_TextChanged(object sender, TextChangedEventArgs e){if (int.TryParse(txtMaxPoints.Text, out int maxPoints)){_maxPoints = maxPoints;// 由于程序初始化时会主动运行一次本事件,此时只要访问chart.Visibility就会卡死if (isChartVisible){SetXTicks(_maxPoints);}}else{txtMaxPoints.Text = _maxPoints.ToString();txtReceivedData.AppendText($"Error:请勿输入非法数据\"{_maxPoints}\"" + Environment.NewLine);}}// 是否禁用动画private void chkDisableAnimation_Checked(object sender, RoutedEventArgs e){chart.DisableAnimations = true;}private void chkDisableAnimation_Unchecked(object sender, RoutedEventArgs e){chart.DisableAnimations = false;}测试程序//private void btnDebug_Click(object sender, RoutedEventArgs e)//{// if (_debugTimer == null)// {// _debugTimer = new System.Timers.Timer(0.01 * 1000);// _debugTimer.Elapsed += DebugTimedEvent;// _debugTimer.AutoReset = true;// }// if (_debugTimer.Enabled)// {// _debugTimer.Enabled = false;// _debugTimer.Dispose();// _debugTimer = null;// }// else// {// _debugTimer.Enabled = true;// }// // 禁用动画后可以显著提高速度// // 检查当前的渲染模式// var currentRenderMode = RenderOptions.ProcessRenderMode;// Console.WriteLine($"Current Render Mode: {currentRenderMode}");// txtReceivedData.AppendText($"Error:请勿输入非法数据\"{currentRenderMode}\"" + Environment.NewLine);//}//private void DebugSendData()//{// debugCount = random.Next(100);// AddDataPoint(debugCount);//}定时事件处理//private async void DebugTimedEvent(object? source, ElapsedEventArgs e)//{// try// {// await Dispatcher.InvokeAsync(DebugSendData);// }// catch (Exception ex)// {// await Dispatcher.InvokeAsync(() =>// {// txtReceivedData.AppendText($"← Debug错误: {ex.Message}" + Environment.NewLine);// });// }//}// 设置X轴数量private void SetXTicks(int tickCount){chart.AxisX.Clear();chart.AxisX.Add(new Axis{MinValue = 1,MaxValue = tickCount,});}// 硬件加速暂时还没搞定// <CheckBox Name = "chkHardwareAcceleration" Content="启用硬件加速" Checked="chkHardwareAcceleration_Checked" Unchecked="chkHardwareAcceleration_Unchecked" />硬件加速//private void chkHardwareAcceleration_Checked(object sender, RoutedEventArgs e)//{// EnableHardwareAcceleration(chart);// // 设置渲染模式// // RenderOptions.ProcessRenderMode = RenderMode.Default;//}//private void chkHardwareAcceleration_Unchecked(object sender, RoutedEventArgs e)//{// DisableHardwareAcceleration(chart);//}//private void EnableHardwareAcceleration(UIElement element)//{// RenderOptions.SetBitmapScalingMode(element, BitmapScalingMode.HighQuality);// RenderOptions.SetEdgeMode(element, EdgeMode.Unspecified);// RenderOptions.SetClearTypeHint(element, ClearTypeHint.Enabled);//}//private void DisableHardwareAcceleration(UIElement element)//{// RenderOptions.SetBitmapScalingMode(element, BitmapScalingMode.LowQuality);// RenderOptions.SetEdgeMode(element, EdgeMode.Aliased);// RenderOptions.SetClearTypeHint(element, ClearTypeHint.Auto);//}} }
2024.11.24
有些昼伏夜出了哈。前面单个文件终究还是有点乱,尝试了以一下MVVM,有些麻烦,最后还是尽量采用MVVM模式。现在添加了保存日志、发送自动换行、刷新串口功能,打开日志目录,波特率、停止位等属性也加了,同时优化了整个界面布局,增加了一些错误检测。当长度输入"-1"时则不进行截取,取全长。
虽然界面现在依旧简陋,但基本功能已经完备了,可以把接收到的普通浮点数据打印在图表上。不过性能嘛,就有些值得考虑考虑了。
由于功能实现得并不多,可以当成一个简陋的框架去使用。目前准备发到github和gitee上,可以当成一个练手的小项目
这是初始界面
这是定时发送数据时
这是打开图表后,100个点,如果以0.01s更新频率,那还是有一些卡的,可能与我使用序列类型和事件聚合器有关,这俩占用还挺大的。有时候禁用动画或许会更流畅一些
这是日志保存,唯一麻烦的是打开日志目录那个功能会被360识别为“危险”(360弹出警告后无法本地截屏),不过好在可以启用360的开发者模式
好吧,忘记加清屏了。现补上,同时测了一下stm32f407无延时发送串口数据,结果是这样的。太快了以至于时间戳都卡没了,后续看看能不能解决
在不使用\r\n的情况下打开图表后就很不正常,可能因为我把图表添加数据点的函数放在了接收数据里。虽然我使用了事件聚合器,把直接调用换成了发布事件,但查了一下,事件聚合器默认是同步的。有空再试一下异步的或者缓存之类的方法
补上了滤除换行的功能,从图中滚动框的长短也可以看出,数据处理框和图表都远远慢于接收框
当单片机每1ms发送一串数据时,不启用处理数据功能,结果还挺好的(除了开头的十个数据)
还有一些测试准备做,太困了,先睡一觉再说
相关文章:
_FYAW智能显示控制仪表的简单使用_串口通信
一、简介 该仪表可以实时显示位移传感器的测量值,并可设定阈值等。先谈谈简单的使用方法,通过说明书,我们可以知道长按SET键可以进入参数选择状态,按“↑”“↓”可以选择该组参数的上一个或者下一个参数。 从参数一览中可以看到有…...
图的遍历。
图的遍历这一部分,离不开广度优先和深度优先,如果大家已经学过搜索算法的话,这部分将是易如反掌。 万能搜索算法-CSDN博客 文章中不会提太多离散数学中图的专有名词,因为本篇博客只涉及最简单的图的遍历,故以练习题为主…...
Methode Electronics EDI 需求分析
Methode Electronics 是一家总部位于美国的全球性技术公司,专注于设计和制造用于多个行业的电子和电气组件,产品涵盖汽车、工业、电信、医疗设备以及消费电子等多个领域,提供创新的解决方案。 填写Methode_EDI_Parameters_Template Methode_…...
IT资产管理工具-NetBox
IT资产管理工具-NetBox 推荐一款IT资产管理工具 了解推荐阅读官方中文文档 https://docs.wangluohe.com/introduction/ 硬件要求 - 建议4Core 8G以上,100G存储空间 这里我使用的Linux镜像为 CentOS8-Stream 提前关闭Selinux和防火墙 部署NetBox 一&#…...
uniapp接入BMapGL百度地图
下面代码兼容安卓APP和H5 百度地图官网:控制台 | 百度地图开放平台 应用类别选择《浏览器端》 /utils/map.js 需要设置你自己的key export function myBMapGL1() {return new Promise(function(resolve, reject) {if (typeof window.initMyBMapGL1 function) {r…...
AWTK 最新动态:支持鸿蒙系统(HarmonyOS Next)
HarmonyOS是全球第三大移动操作系统,有巨大的市场潜力,在国产替代的背景下,机会多多,AWTK支持HarmonyOS,让AWTK开发者也能享受HarmonyOS生态的红利。 AWTK全称为Toolkit AnyWhere,是ZLG倾心打造的一套基于C…...
React基础知识一
写的东西太多了,照成csdn文档编辑器都开始卡顿了,所以分篇写。 1.安装React 需要安装下面三个包。 react:react核心包 react-dom:渲染需要用到的核心包 babel:将jsx语法转换成React代码的工具。(没使用jsx可以不装)1.1 在html中…...
Oracle热备过程中对数据库崩溃的处理方法
引言 在热备过程中如果发生数据库崩溃、断电等情况该如何处理? 如果正在备份 users 表空间的数据文件过程中,此时的数据文件表头 SCN 会被锁定,此时正在复制数据文件时数据库崩溃,系统断电。 从而导致数据文件表头与控制文件中的不一致,导致数据库无法打开,会要求介质恢…...
身份证实名认证API接口助力电商购物安全
亲爱的网购达人们,你们是否曾经因为网络上的虚假信息和诈骗而感到困扰?在享受便捷的网购乐趣时,如何确保交易安全成为了我们共同关注的话题。今天,一起来了解一下翔云身份证实名认证接口如何为电子商务保驾护航,让您的…...
win10 禁止更新
一、winR 输入 regedit 二、输入注册列表路径: (1)计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings (2)按照格式,创建文件命名: FlightSettingsMaxPauseDays (3&…...
运维百科:网络性能20大关键指标
网络性能评估是确保网络服务质量和用户体验的关键环节。在网络、运维领域中,存在着一系列关键的性能指标,共同构成了衡量网络性能的基础。以下是网络性能的20大关键指标,每个指标都承载着特定的意义和重要性。 1.速率(Rate&#…...
java编程开发基础,正则表达式的使用案例Demo
java编程开发基础,正则表达式的使用案例Demo!实际开发中,经常遇到一些字符串,信息的裁剪和提取操作,正则表达式是经常使用的,下面的案例,可以帮助大家快速的了解和熟悉,正则表达式的使用技巧。 package com…...
结构控制
目录 1.顺序结构 2.分支结构 2.1.单分支结构 2.2.二分支结构 2.3.多分支结构 2.4.嵌套分支结构 3.循环结构 3.1.while 循环结构 3.2.while...else 循环结构 PS:break 关键字 PS:pass 关键字 3.3.for 循环结构 PS:…...
Go语言中的内存分配与初始化:new与make函数详解
在Go语言中,内存分配和初始化是编程的基础操作。Go提供了两个内置函数new和make,用于不同场景下的内存分配和初始化。理解这两个函数的区别和适用场景对于编写高效、安全的Go代码至关重要。本文将详细介绍new和make函数,并提供示例说明它们的…...
The 2024 ICPC Kunming Invitational Contest
VP链接:https://codeforces.com/gym/105386 B. Gold Medal 签到题。对每一个读入的数 a,先记录已有奖牌数量,即 ,再将 a 对 k 取模。然后将 a 数组从大到小排序,将每个不足 k 的数补到 k。如果 m 有剩余,…...
对原jar包解压后修改原class文件后重新打包为jar
文章目录 背景三种修改方式1.POM中移除原jar中依赖的历史版本2.原jar它不使用pom依赖而是直接放在源码中再编译使用JarEditor 插件对源码进行修改(推荐)使用java-decompiler反编译后修改源码覆盖原class(不好用-不推荐直接跳过)提醒 参考资料-推荐阅读拓…...
【C++】ReadFile概述,及实践使用时ReadFile的速率影响研究
ReadFile 函数概述 ReadFile 是 Windows API 函数,用于从文件或设备(如串口、硬盘等)中读取数据。它是同步和异步 I/O 操作的基础函数。 函数原型 BOOL ReadFile(_In_ HANDLE hFile, // 文件或设备句柄_Out_write…...
WebGL进阶(十一)层次模型
理论基础: 效果: 源码: <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"vie…...
Django:从入门到精通
一、Django背景 Django是一个由Python编写的高级Web应用框架,以其简洁性、安全性和高效性而闻名。Django最初由Adrian Holovaty和Simon Willison于2003年开发,旨在简化Web应用的开发过程。作为一个开放源代码项目,Django迅速吸引了大量的开发…...
C++设计模式行为模式———中介者模式
文章目录 一、引言二、中介者模式三、总结 一、引言 中介者模式是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。 中介者模式可以减少对象之间混乱无序的依赖关系&…...
Perfetto学习大全
Perfetto 是一个功能强大的性能分析和追踪工具,主要用于捕获和分析复杂系统中的事件和性能数据,特别是在 Android 和 Linux 环境下。它的核心目标是帮助开发者深入了解系统和应用程序的运行状态,以便优化性能和诊断问题。 Perfetto的主要作用…...
管家婆财贸ERP BR040.销售单明细表变更
最低适用版本: C系列 23.8 插件简要功能说明: 销售明细表支持直接修改单据自由项1-16更多细节描述见下方详细文档 插件操作视频: 进销存类定制插件--销售单明细表变更 1. 应用中心增加菜单【销售单明细表变更】 a. 复制23.8版本销售单明细…...
2411rust,实现特征
原文 在Rust2024中,impl Trait在中位置的默认工作方式有了变化.是为了简化impl Trait,以更好地匹配人们一般的需求. 还添加了一个灵活的语法,让你需要时可完全控制. 从Rust2024开始,一直在更改,何时可在返回位置impl Trait的隐藏类型中使用泛型参数的规则: 1,即对返回位置i…...
SpringBoot3与JUnit5集成测试
你可以在 Spring Boot 3 中轻松设置和运行 JUnit 集成测试。合理使用 Spring 提供的注解和工具,可以确保测试的高效性和可靠性。以下是集成测试的步骤和示例: 1. 添加依赖 在 pom.xml 中添加 Spring Boot Starter Test 依赖,它包含 JUnit 5 …...
工程企业需要什么样的物资管理系统?为什么需要物资管理系统?
一、背景与意义 在工程项目的建设中,无论是高楼大厦的拔地而起,还是高速公路的绵延铺展,物资都是最基础的要素之一。从钢筋水泥到施工机械,任何一种物资的管理不善都可能导致项目延误、成本超支,甚至质量问题。然而&a…...
Vue + Websocket播放PCM(base64转ArrayBuffer、 字符串转ArrayBuffer)
文章目录 引言I 音视频处理相关概念和APIII 案例:基于开源库 pcm-player方式播放借助MediaSource和Audio对象播放音频流。基于原生api AudioContext 播放操作III 格式转换js字符串转ArrayBufferbase64 转 ArrayBufferIV 解决pcm-player分片播放问题引言 需求: 基于webscoket传…...
华为防火墙技术基本概念学习笔记
1.防火墙概述 1.1防火墙与交换机、路由器对比 路由器与交换机的本质是转发,防火墙的本质是控制。 防火墙与路由器、交换机是有区别的。路由器用来连接不同的网络,通过路由协议保证互联互通,确保将报文转发到目的地;交换机则通常用来组建局域…...
Mesh路由组网
Mesh无线网格网络,多跳(multi-hop)网络,为解决全屋覆盖信号,一般用于家庭网络和小型企业 原理 网关路由器(主路由,连接光猫),Mesh路由器(子路由,…...
【数据结构】七种常用排序总结
一、七种排序及其讲解 以下为七种排序的讲解: 【数据结构】插入排序——直接插入排序 和 希尔排序 【数据结构】选择排序——选择排序 和 堆排序 【数据结构】交换排序——冒泡排序 和 快速排序 【数据结构】归并排序 —— 递归及非递归解决归并排序 二、排序的…...
【在Linux世界中追寻伟大的One Piece】多线程(一)
目录 1 -> Linux线程概念 1.1 -> 什么是线程 1.2 -> 线程的优点 1.3 -> 线程的缺点 1.4 -> 线程异常 1.5 -> 线程用途 2 -> Linux线程 VS 进程 2.1 -> 线程和进程 2.2 -> 进程的多个线程共享 3 -> Linux线程控制 3.1 -> POSIX线程…...
《Python编程实训快速上手》第十天--处理CSV文件和JSON数据
CSV:简化的电子表格,被保存为纯文本文件 JSON:是一种数据交换格式,易于人阅读和编写,同时也易于机器解析和生成,以JavaScript源代码的形式将信息保存在纯文本文件中 一、csv模块 CSV文件中的每行代表电…...
基于springboot停车场管理系统源码和论文
如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统停车场管理系统信息管理难度大,容错率低,…...
Linux的桌面
Linux的桌面是可以卸载的 的确,Linux并不像Windows,Linux本身是一个基于命令行的操作系统,在内核眼中,桌面只不过是个普通的应用程序,所以,在Linux的桌面中可以完成的事情,命令行中也基本可以完…...
Spring Boot 3.0废弃了JavaEE,改用了Jakarta EE
Spring Boot 3.0废弃了JavaEE,改用了Jakarta EE 历史背景 javax变成Jakarta的主要原因是因为Java EE项目从Oracle转移到了Eclipse Foundation,并改名为Jakarta EE。 JavaEE是从Java 1.2版本开始推出的Java企业级开发平台,最初的名称是J2EE(J…...
java-排序算法汇总
排序算法: 冒泡排序(Bubble Sort) 选择排序(Selection Sort) 插入排序(Insertion Sort) 快速排序(Quick Sort) 归并排序(Merge Sort) 堆排序&…...
C0030.Clion中运行提示Process finished with exit code -1073741515 (0xC0000135)解决办法
1.错误提示 2.解决办法 添加环境变量完成之后,重启Clion软件,然后就可以正常调用由mingw编译的opencv库了。...
如何理解JS的 异步
JS是一门单线程的语言,这是因为它运行在浏览器的渲染主线程中,而渲染主线程只有一个。 而渲染主线程承担着诸多的工作,渲染页面、执行JS都在其中运行。 如果使用同步的方式,就极有可能导致主线程产生阻塞,从而导致消…...
vsgithub
VS&GitHub项目联动(上传和克隆),创建你的第一个仓库,小白配置_vs上传代码到github-CSDN博客...
Android蓝牙架构,源文件目录/编译方式学习
Android 版本 发布时间 代号(Codename) Android 1.0 2008年9月23日 无 Android 1.1 2009年2月9日 Petit Four Android 1.5 2009年4月27日 Cupcake Android 1.6 2009年9月15日 Donut Android 2.0 2009年10月26日 Eclair Android 2.1 2…...
10 - Clickhouse集群部署以及副本和分片
目 一、副本 1、简介 2、副本写入流程 3、副本配置步骤 3.1、启动zookeeper集群 3.2、在 hallo100 的/etc/clickhouse-server/config.d 目录下创建一个名为metrika.xml 的配置文件,内容如下: 3.3、在 hallo100 的/etc/clickhouse-server/config.xml 中增加如…...
FreeRTOS的软件定时器与事件标志组
目录 1.FreeRTOS软件定时器 1.1 什么是FreeRTOS软件定时器? 1.2 学习软件定时器的意义 1.3 软件定时器的简介 1.3.1 软件定时器概述 1.3.2 编写回调函数的注意事项 1.4 定时器服务/Daemon 任务 1.4.1 定时器服务任务与队列 1.4.2 定时器相关配置 configUSE_T…...
生产制造领域的多元化模式探索
在当今全球化和信息化的时代背景下,生产制造领域正经历着前所未有的变革。随着消费者需求的多样化、市场竞争的加剧以及技术的不断进步,传统的生产制造模式已经难以满足现代企业的需求。因此,多种生产制造模式应运而生,以适应不同…...
大数据技术之SparkCore
RDD概述 什么是RDD RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象。代码中是一个抽象类,它代表一个弹性的、不可变、可分区、里面的元素可并行计算的集合。 RDD五大特性 RDD编程 RDD的创…...
element-plus的组件数据配置化封装 - table
目录 一、封装的table、table-column组件以及相关ts类型的定义 1、ATable组件的封装 - index.ts 2、ATableColumn组件的封装 - ATableColumn.ts 3、ATable、ATableColumn类型 - interface.ts 二、ATable、ATableColumn组件的使用 三、相关属性、方法的使用以及相关说明 1. C…...
蓝桥杯每日真题 - 第15天
题目:(钟表) 题目描述(13届 C&C B组B题) 解题思路: 理解钟表指针的运动: 秒针每分钟转一圈,即每秒转6度。 分针每小时转一圈,即每分钟转6度。 时针每12小时转一圈…...
c#:winform调用bartender实现打印(学习整理笔记)
效果 学习路径 C# winform调用Bartender进行自定义打印、批量打印、检索文件夹中的模板_哔哩哔哩_bilibili 一、初始环境搭建见: c#:winform引入bartender-CSDN博客https://blog.csdn.net/weixin_46001736/article/details/143989473?sharetypeblogdetail&s…...
周期法频率计的设计
目录 周期法频率计 分析: 设计过程: 周期法频率计 对于低频信号,应用周期法进行测频。周期法测频的基本原理是:应用标准频率信号统计被测信号两个相邻脉冲之间的脉冲数,然后通过脉冲数计算出被测信号的周期ÿ…...
【2024亚太杯亚太赛APMCM C题】数学建模竞赛|宠物行业及相关产业的发展分析与策略|建模过程+完整代码论文全解全析
第一个问题是:请基于附件 1 中的数据以及你的团队收集的额外数据,分析过去五年中国宠物行业按宠物类型的发展情况。并分析中国宠物行业发展的因素,预测未来三年中国宠物行业的发展。 第一个问题:分析中国宠物行业按宠物类型的发展…...
uniapp的renderjs使用
uniapp中的RenderJS主要服务于APP和H5平台,其作用包括降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力,以及在视图层操作DOM和运行Web的JS库。 RenderJS是uni-app中一个特性,它允许开发者在页面中使用JavaScript直接渲…...
CPU命名那些事
一、Intel CPU命名 1. 命名结构 Intel CPU 的命名通常包含以下几个部分: 品牌 产品线 系列 代数 具体型号 后缀 例如:Intel Core i7-13700K 2. 各部分含义 品牌 Intel:表示厂商(几乎所有命名中都有)。不同品…...