乐于分享
好东西不私藏

【技术分享】十分钟,AI帮你写一个串口助手调试软件

【技术分享】十分钟,AI帮你写一个串口助手调试软件
目前,AI已经向许多领域进行渗透,软件开发也不例外,软件开发工程师如何利用AI增效?我今天做了一个尝试,如下:
一、题目:《用Trae AI生成一个基于WinForm架构的串口助手调试工具》
二、所用工具
TraeCode、VS2022
三、步骤
第一步:下载TraeWork。链接分享如下:
我在用 TraeWork AI 办公平台,搞定各类任务,通过我的分享链接登录桌面端,新注册用户一次性获得 5000 积分! https://www.trae.cn/work-fission/DY58TU63HEEY?utm_source=copy_link&utm_medium=friends_invite
TRAE Work是字节跳动推出的AI原生工作台,由TRAE SOLO升级而来。
第二步,安装TraeWork,此处需要注册。具体安装方法我就不细说了。
第三步,打开TraeWork。其界面如下:
第四步,在右下角的智能体对话框里面输入你的项目要求,比如我输入的是“用WinForm生成一个串口助手工具”,此时你需要选择一个目标文件夹。
第五步,等待10分钟左右(有的朋友网速快,可能时间更短),便会生成一套源码程序,在目标文件夹里面打开源程序,编译即可得到所要的串口助手。如下:
四、AI生成的部分源代码展示
public partial class MainForm : Form{    private readonly SerialPort _port = new SerialPort();    private long _rxCount;    private long _txCount;    // 用于在 HEX显示 关闭时累积可打印字符    private readonly StringBuilder _asciiBuf = new StringBuilder();    publicMainForm()    {        InitializeComponent();        WireEvents();        PopulateBaudRates();        PopulateDataBits();        PopulateParity();        PopulateStopBits();        RefreshPorts();        UpdateConnectionState();    }    // ===== 事件绑定 =====    privatevoidWireEvents()    {        btnRefresh.Click += (s, e) => RefreshPorts();        btnOpenClose.Click += (s, e) => ToggleConnection();        btnSend.Click += (s, e) => DoSend();        btnClearTx.Click += (s, e) => txtSend.Clear();        btnClearRx.Click += (s, e) =>        {            txtReceive.Clear();            _asciiBuf.Clear();        };        btnSaveRx.Click += (s, e) => SaveReceived();        btnResetCount.Click += (s, e) =>        {            _rxCount = 0;            _txCount = 0;            UpdateCounters();        };        chkRxHex.CheckedChanged += (s, e) =>        {            _asciiBuf.Clear();        };        chkAutoWrap.CheckedChanged += (s, e) =>        {            txtReceive.WordWrap = chkAutoWrap.Checked;        };        chkAutoSend.CheckedChanged += (s, e) =>        {            if (chkAutoSend.Checked)            {                if (!_port.IsOpen)                {                    chkAutoSend.Checked = false;                    MessageBox.Show("请先打开串口再启用自动发送。", "提示",                        MessageBoxButtons.OK, MessageBoxIcon.Information);                    return;                }                tmrAutoSend.Interval = (int)numInterval.Value;                tmrAutoSend.Start();            }            else            {                tmrAutoSend.Stop();            }        };        numInterval.ValueChanged += (s, e) =>        {            if (tmrAutoSend.Enabled)            {                tmrAutoSend.Interval = (int)numInterval.Value;            }        };        tmrAutoSend.Tick += (s, e) => DoSend();        txtSend.KeyDown += (s, e) =>        {            if (e.Control && e.KeyCode == Keys.Enter)            {                DoSend();                e.SuppressKeyPress = true;            }        };        _port.DataReceived += OnDataReceived;        this.FormClosing += (s, e) =>        {            if (_port.IsOpen)            {                try { _port.Close(); } catch { }            }        };    }    // ===== 下拉框填充 =====    privatevoidPopulateBaudRates()    {        cmbBaud.Items.Clear();        int[] rates = { 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 };        foreach (var r in rates) cmbBaud.Items.Add(r);        cmbBaud.SelectedItem = 115200;    }    privatevoidPopulateDataBits()    {        cmbDataBits.Items.Clear();        foreach (var d in new[] { 5, 6, 7, 8 }) cmbDataBits.Items.Add(d);        cmbDataBits.SelectedItem = 8;    }    privatevoidPopulateParity()    {        cmbParity.Items.Clear();        cmbParity.Items.Add("None");        cmbParity.Items.Add("Odd");        cmbParity.Items.Add("Even");        cmbParity.Items.Add("Mark");        cmbParity.Items.Add("Space");        cmbParity.SelectedIndex = 0;    }    privatevoidPopulateStopBits()    {        cmbStopBits.Items.Clear();        cmbStopBits.Items.Add("1");        cmbStopBits.Items.Add("1.5");        cmbStopBits.Items.Add("2");        cmbStopBits.SelectedIndex = 0;    }    privatevoidRefreshPorts()    {        string prev = cmbPort.SelectedItem?.ToString() ?? "";        cmbPort.Items.Clear();        try        {            var names = SerialPort.GetPortNames();            Array.Sort(names, StringComparer.OrdinalIgnoreCase);            foreach (var n in names) cmbPort.Items.Add(n);            if (cmbPort.Items.Count == 0)            {                cmbPort.Items.Add("COM1");            }            if (cmbPort.Items.Contains(prev))                cmbPort.SelectedItem = prev;            else if (cmbPort.Items.Count > 0)                cmbPort.SelectedIndex = 0;        }        catch (Exception ex)        {            AppendStatusLine("[刷新端口失败] " + ex.Message);        }    }    // ===== 打开/关闭串口 =====    privatevoidToggleConnection()    {        if (_port.IsOpen)        {            ClosePort();        }        else        {            OpenPort();        }    }    privatevoidOpenPort()    {        if (cmbPort.SelectedItem == null)        {            MessageBox.Show("请选择串口号。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);            return;        }        try        {            // 关闭自动发送            if (chkAutoSend.Checked)            {                chkAutoSend.Checked = false;            }            _port.PortName = cmbPort.SelectedItem.ToString()!;            _port.BaudRate = Convert.ToInt32(cmbBaud.SelectedItem);            _port.DataBits = Convert.ToInt32(cmbDataBits.SelectedItem);            _port.Parity = ParseParity(cmbParity.SelectedItem.ToString()!);            _port.StopBits = ParseStopBits(cmbStopBits.SelectedItem.ToString()!);            _port.ReadTimeout = 500;            _port.WriteTimeout = 500;            _port.ReceivedBytesThreshold = 1;            _port.Open();            UpdateConnectionState();            AppendStatusLine($"[已打开 {_port.PortName} @ {_port.BaudRate}]");        }        catch (Exception ex)        {            MessageBox.Show("打开串口失败:\r\n" + ex.Message, "错误",                MessageBoxButtons.OK, MessageBoxIcon.Error);        }    }    privatevoidClosePort()    {        try        {            if (chkAutoSend.Checked) chkAutoSend.Checked = false;            _port.Close();            UpdateConnectionState();            AppendStatusLine("[串口已关闭]");        }        catch (Exception ex)        {            MessageBox.Show("关闭串口失败:\r\n" + ex.Message, "错误",                MessageBoxButtons.OK, MessageBoxIcon.Error);        }    }    privatevoidUpdateConnectionState()    {        if (_port.IsOpen)        {            btnOpenClose.Text = "关闭串口";            btnOpenClose.BackColor = Color.FromArgb(220, 53, 69);            tslStatus.Text = " ● 已连接 ";            tslStatus.ForeColor = Color.FromArgb(0, 150, 80);            SetConfigEnabled(false);        }        else        {            btnOpenClose.Text = "打开串口";            btnOpenClose.BackColor = Color.FromArgb(0, 120, 215);            tslStatus.Text = " ● 未连接 ";            tslStatus.ForeColor = Color.Gray;            SetConfigEnabled(true);        }    }    privatevoidSetConfigEnabled(bool enabled)    {        cmbPort.Enabled = enabled;        cmbBaud.Enabled = enabled;        cmbDataBits.Enabled = enabled;        cmbParity.Enabled = enabled;        cmbStopBits.Enabled = enabled;        btnRefresh.Enabled = enabled;    }    // ===== 发送 =====    privatevoidDoSend()    {        if (!_port.IsOpen)        {            if (chkAutoSend.Checked) chkAutoSend.Checked = false;            return;        }        string text = txtSend.Text;        if (string.IsNullOrEmpty(text)) return;        byte[] data = chkTxHex.Checked            ? HexStringToBytes(text)            : Encoding.ASCII.GetBytes(text);        if (data.Length == 0) return;        try        {            if (chkSendNewLine.Checked && !chkTxHex.Checked)            {                byte[] withNl = new byte[data.Length + 2];                Array.Copy(data, 0, withNl, 0, data.Length);                withNl[data.Length] = (byte)'\r';                withNl[data.Length + 1] = (byte)'\n';                data = withNl;            }            _port.Write(data, 0, data.Length);            _txCount += data.Length;            UpdateCounters();        }        catch (Exception ex)        {            AppendStatusLine("[发送失败] " + ex.Message);            if (chkAutoSend.Checked) chkAutoSend.Checked = false;        }    }    // ===== 接收 (串口线程 -> UI 线程) =====    privatevoidOnDataReceived(object sender, SerialDataReceivedEventArgs e)    {        if (_port == null || !_port.IsOpen) return;        try        {            int n = _port.BytesToRead;            if (n <= 0) return;            byte[] buf = new byte[n];            _port.Read(buf, 0, n);            _rxCount += n;            this.BeginInvoke((Action)(() => DisplayReceived(buf)));        }        catch (Exception ex)        {            this.BeginInvoke((Action)(() =>                AppendStatusLine("[接收失败] " + ex.Message)));        }    }    privatevoidDisplayReceived(byte[] buf)    {        UpdateCounters();        string chunk;        if (chkRxHex.Checked)        {            chunk = BytesToHexString(buf) + " ";            _asciiBuf.Clear();            AppendReceiveText(chunk, Color.FromArgb(0, 100, 0));        }        else        {            // ASCII / GBK 解码尝试            string s;            try { s = Encoding.GetEncoding("GBK").GetString(buf); }            catch { s = Encoding.ASCII.GetString(buf); }            if (chkShowTime.Checked)            {                AppendReceiveText($"[{DateTime.Now:HH:mm:ss.fff}] ", Color.FromArgb(120, 120, 120));            }            AppendReceiveText(s, Color.Black);        }        // 限制接收区长度, 避免内存膨胀        if (txtReceive.TextLength > 1_000_000)        {            txtReceive.Select(0, txtReceive.TextLength - 500_000);            txtReceive.SelectedText = "";        }    }    privatevoidAppendReceiveText(string text, Color color)    {        txtReceive.SelectionStart = txtReceive.TextLength;        txtReceive.SelectionLength = 0;        txtReceive.SelectionColor = color;        txtReceive.AppendText(text);        txtReceive.SelectionColor = txtReceive.ForeColor;        txtReceive.ScrollToCaret();    }    privatevoidAppendStatusLine(string text)    {        AppendReceiveText(text + "\r\n", Color.FromArgb(180, 80, 0));    }    // ===== 计数器 =====    privatevoidUpdateCounters()    {        tslRxCount.Text = $" RX: {_rxCount:N0} ";        tslTxCount.Text = $" TX: {_txCount:N0} ";    }    // ===== 保存接收数据 =====    privatevoidSaveReceived()    {        if (txtReceive.TextLength == 0)        {            MessageBox.Show("接收区无数据可保存。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);            return;        }        using (var sfd = new SaveFileDialog())        {            sfd.Filter = "文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*";            sfd.FileName = $"serial_rx_{DateTime.Now:yyyyMMdd_HHmmss}.txt";            if (sfd.ShowDialog(this) == DialogResult.OK)            {                try                {                    File.WriteAllText(sfd.FileName, txtReceive.Text, Encoding.UTF8);                    AppendStatusLine("[已保存到 " + sfd.FileName + "]");                }                catch (Exception ex)                {                    MessageBox.Show("保存失败:\r\n" + ex.Message, "错误",                        MessageBoxButtons.OK, MessageBoxIcon.Error);                }            }        }    }    // ===== 工具方法 =====    privatestatic Parity ParseParity(string s)    {        return s switch        {            "Odd" => Parity.Odd,            "Even" => Parity.Even,            "Mark" => Parity.Mark,            "Space" => Parity.Space,            _ => Parity.None,        };    }    privatestatic StopBits ParseStopBits(string s)    {        return s switch        {            "1.5" => StopBits.OnePointFive,            "2" => StopBits.Two,            _ => StopBits.One,        };    }    privatestaticstringBytesToHexString(byte[] buf)    {        var sb = new StringBuilder(buf.Length * 3);        for (int i = 0; i < buf.Length; i++)        {            if (i > 0) sb.Append(' ');            sb.Append(buf[i].ToString("X2"));        }        return sb.ToString();    }    privatestaticbyte[] HexStringToBytes(string text)    {        // 移除空白、换行、0x 前缀等        string cleaned = text            .Replace("0x", "")            .Replace("0X", "")            .Replace(" ", "")            .Replace("\r", "")            .Replace("\n", "")            .Replace("\t", "")            .Replace(",", "");        if ((cleaned.Length % 2) != 0)        {            cleaned = "0" + cleaned;        }        var bytes = new byte[cleaned.Length / 2];        for (int i = 0; i < bytes.Length; i++)        {            bytes[i] = Convert.ToByte(cleaned.Substring(i * 2, 2), 16);        }        return bytes;    }
看了AI生成的源代码,感觉还可以,AI写的已经很像人写的了。
五、缺点
1、没有发送按钮,后面需要人为调整;
六、总结
1、本次实验只做了C#上位机程序的实验,由于没有实验环境,没有进行代码正确性的验证,但是看代码内容,应该是没有什么问题的。
2、对于Qt的测试没有进行,本打算用Qt进行实验的,但是机器上没有装。
3、对于Agent的使用还没有完全熟悉,不知道对于大型软件工程和嵌入式编程来说是否能够一样的提高产出。待后续研究。。。

相关学习资料