西门子 S7 PLC 通信 WPF 应用分析笔记

西门子 S7 PLC 通信 WPF 应用分析笔记

1. 项目概述

  • 技术栈
    • WPF(Windows Presentation Foundation)用于界面展示。
    • MVVM (Model-View-ViewModel)设计模式,通过 GalaSoft.MvvmLight 实现。
    • S7.Net 库用于与西门子 S7 系列 PLC 进行以太网通信。
  • 功能
    • 配置 PLC 连接参数(IP、机架号、槽号)。
    • 连接 / 断开 PLC。
    • 从指定地址读取数据。
    • 向指定地址写入数据。
    • 记录操作日志。

2. 整体架构(MVVM 模式)

View(视图)
  • 文件MainWindow.xaml
  • 作用
    • 定义界面布局和控件。
    • 通过 数据绑定 将控件与 ViewModel 的属性关联。
    • 通过 命令绑定 将按钮点击事件绑定到 ViewModel 的命令。
ViewModel(视图模型)
  • 文件MainViewModel.cs
  • 作用
    • 持有界面所需的数据(连接参数、读写地址、结果、日志等)。
    • 实现业务逻辑(连接、断开、读写 PLC)。
    • 通过 INotifyPropertyChanged 通知 View 更新界面。
    • 通过 RelayCommand 处理用户操作。
Model(模型)
  • 外部库S7.Net
  • 作用
    • 提供 Plc 类实现与 S7 PLC 的底层通信。
    • 封装了 TCP/IP 连接、数据读写等功能。

3. 界面(View)分析

主要区域
  1. 标题
    • 显示 "西门子S7 PLC通信"
  2. 连接设置
    • IP 地址 (绑定 IpAddress)。
    • 机架号 (绑定 Rack)。
    • 槽号 (绑定 Slot)。
    • 连接状态 (绑定 ConnectionStatus)。
    • 连接按钮 (绑定 ConnectCommand,仅在未连接时可用)。
    • 断开按钮 (绑定 DisconnectCommand,仅在已连接时可用)。
  3. 读取操作
    • 读取地址 (绑定 ReadAddress)。
    • 读取按钮 (绑定 ReadCommand,仅在已连接时可用)。
    • 读取结果 (绑定 ReadValue,只读)。
  4. 写入操作
    • 写入地址 (绑定 WriteAddress)。
    • 写入数值 (绑定 WriteValue)。
    • 写入按钮 (绑定 WriteCommand,仅在已连接时可用)。
  5. 操作日志
    • 显示程序运行日志(绑定 Log,只读)。
绑定方式
  • 数据绑定

    xml

    复制代码
    <TextBox Text="{Binding IpAddress}" />
  • 命令绑定

    xml

    复制代码
    <Button Command="{Binding ConnectCommand}" />
  • 按钮可用性绑定

    xml

    复制代码
    <Button IsEnabled="{Binding !IsConnected}" />

4. 视图模型(ViewModel)分析

4.1 数据属性
  • 连接参数

    csharp

    运行

    复制代码
    public string IpAddress { get; set; }
    public short Rack { get; set; }
    public short Slot { get; set; }
  • 状态属性

    csharp

    运行

    复制代码
    public string ConnectionStatus { get; set; }
    public bool IsConnected { get; set; }
  • 读写相关

    csharp

    运行

    复制代码
    public string ReadAddress { get; set; }
    public string ReadValue { get; set; }
    public string WriteAddress { get; set; }
    public string WriteValue { get; set; }
  • 日志

    csharp

    运行

    复制代码
    public string Log { get; set; }
4.2 命令
  • ConnectCommand:连接 PLC。
  • DisconnectCommand:断开 PLC。
  • ReadCommand:读取数据。
  • WriteCommand:写入数据。
4.3 核心方法
AddLog

csharp

运行

复制代码
private void AddLog(string message)
{
    Log = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}\n{Log}";
}
  • 在日志前添加时间戳。
  • 新日志显示在最上方。
Connect

csharp

运行

复制代码
private async Task Connect()
{
    try
    {
        AddLog($"正在连接PLC: {IpAddress}, 机架: {Rack}, 槽号: {Slot}");
        _plc = new Plc(CpuType.S71200, IpAddress, Rack, Slot);
        await Task.Run(() => _plc.Open());

        IsConnected = _plc.IsConnected;
        ConnectionStatus = IsConnected ? "已连接" : "连接失败";
        AddLog(ConnectionStatus);
    }
    catch (Exception ex)
    {
        ConnectionStatus = "连接错误: " + ex.Message;
        IsConnected = false;
        AddLog(ConnectionStatus);
        MessageBox.Show(ex.Message, "连接错误", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}
  • 创建 Plc 对象并调用 Open() 连接。
  • 异步执行防止 UI 阻塞。
  • 更新连接状态并记录日志。
  • 异常处理:提示错误信息。
Disconnect

csharp

运行

复制代码
private async Task Disconnect()
{
    try
    {
        if (_plc != null && _plc.IsConnected)
        {
            AddLog("正在断开PLC连接");
            await Task.Run(() => _plc.Close());

            IsConnected = false;
            ConnectionStatus = "已断开";
            AddLog(ConnectionStatus);
        }
    }
    catch (Exception ex)
    {
        ConnectionStatus = "断开错误: " + ex.Message;
        AddLog(ConnectionStatus);
        MessageBox.Show(ex.Message, "断开错误", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}
  • 调用 Close() 断开连接。
  • 更新状态并记录日志。
ReadFromPLC

csharp

运行

复制代码
private async Task ReadFromPLC()
{
    try
    {
        if (_plc != null && _plc.IsConnected)
        {
            AddLog($"正在读取PLC地址: {ReadAddress}");
            var value = await Task.Run(() => _plc.Read(ReadAddress));

            ReadValue = value?.ToString() ?? "读取失败";
            AddLog($"读取成功: {ReadValue}");
        }
        else
        {
            AddLog("读取失败: 请先连接PLC");
            MessageBox.Show("请先连接PLC", "错误", MessageBoxButton.OK, MessageBoxImage.Warning);
        }
    }
    catch (Exception ex)
    {
        ReadValue = "读取错误: " + ex.Message;
        AddLog(ReadValue);
        MessageBox.Show(ex.Message, "读取错误", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}
  • 检查连接状态。
  • 调用 _plc.Read() 读取数据。
  • 更新 ReadValue 并记录日志。
WriteToPLC

csharp

运行

复制代码
private async Task WriteToPLC()
{
    try
    {
        if (_plc != null && _plc.IsConnected)
        {
            if (double.TryParse(WriteValue, out double numericValue))
            {
                AddLog($"正在写入PLC地址: {WriteAddress}, 值: {numericValue}");
                await Task.Run(() => _plc.Write(WriteAddress, numericValue));

                AddLog("写入成功");
                MessageBox.Show("写入成功", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
            }
            else
            {
                AddLog("写入失败: 请输入有效的数值");
                MessageBox.Show("请输入有效的数值", "错误", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }
        else
        {
            AddLog("写入失败: 请先连接PLC");
            MessageBox.Show("请先连接PLC", "错误", MessageBoxButton.OK, MessageBoxImage.Warning);
        }
    }
    catch (Exception ex)
    {
        AddLog($"写入错误: {ex.Message}");
        MessageBox.Show(ex.Message, "写入错误", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}
  • 检查连接状态。
  • 验证输入是否为有效的 double
  • 调用 _plc.Write() 写入数据。
  • 记录日志并提示结果。

5. 关键技术点

  1. MVVM 模式
    • 分离界面与业务逻辑,提高可维护性。
  2. 数据绑定
    • View 与 ViewModel 自动同步数据。
  3. 命令模式
    • RelayCommand 实现按钮点击事件与 ViewModel 方法绑定。
  4. 异步编程
    • async/await 防止 UI 阻塞。
  5. S7.Net
    • 简化与西门子 S7 PLC 的通信。
  6. 日志记录
    • 实时显示操作过程和错误信息。

6. 运行流程示例

  1. 用户输入 IP 地址机架号槽号
  2. 点击 连接 按钮:
    • ConnectCommand 调用 Connect() 方法。
    • 程序尝试连接 PLC。
    • 更新连接状态并记录日志。
  3. 输入 读取地址 ,点击 读取
    • ReadCommand 调用 ReadFromPLC()
    • 从 PLC 读取数据并显示结果。
  4. 输入 写入地址写入数值 ,点击 写入
    • WriteCommand 调用 WriteToPLC()
    • 将数据写入 PLC 并提示结果。
  5. 点击 断开 按钮:
    • DisconnectCommand 调用 Disconnect()
    • 断开与 PLC 的连接。

7. 改进建议

  1. 日志持久化
    • 将日志保存到文件,方便后续分析。
  2. 数据类型支持
    • 支持更多数据类型(如 int, bool)。
  3. 批量读写
    • 支持一次性读写多个地址。
  4. 错误处理优化
    • MessageBox 替换为界面内的错误提示,提升用户体验。
  5. UI 美化
    • 使用 MahApps.Metro 等库美化界面。

总结 :该项目是一个基于 WPF 和 MVVM 模式的西门子 S7 PLC 通信工具,通过 S7.Net 库实现了 PLC 的连接、数据读取和写入功能,并提供了友好的日志记录功能。代码结构清晰,易于扩展和维护。

完整代码:

cs 复制代码
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using S7.Net;
using System;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApp1.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        // PLC 核心对象
        private Plc _plc;

        // 连接配置属性
        private string _ipAddress = "192.168.0.1";
        private short _rack = 0;
        private short _slot = 1;

        // 状态属性
        private string _connectionStatus = "未连接";
        private bool _isConnected;

        // 读写相关属性
        private string _readValue;
        private string _writeValue;
        private string _readAddress = "DB1.DBD0";
        private string _writeAddress = "DB1.DBD0";

        // 日志属性
        private string _log;

        // 公共属性(INotifyPropertyChanged 支持)
        public string Log
        {
            get => _log;
            set => Set(ref _log, value);
        }

        public string IpAddress
        {
            get => _ipAddress;
            set => Set(ref _ipAddress, value);
        }

        public short Rack
        {
            get => _rack;
            set => Set(ref _rack, value);
        }

        public short Slot
        {
            get => _slot;
            set => Set(ref _slot, value);
        }

        public string ConnectionStatus
        {
            get => _connectionStatus;
            set => Set(ref _connectionStatus, value);
        }

        public bool IsConnected
        {
            get => _isConnected;
            set => Set(ref _isConnected, value);
        }

        public string ReadValue
        {
            get => _readValue;
            set => Set(ref _readValue, value);
        }

        public string WriteValue
        {
            get => _writeValue;
            set => Set(ref _writeValue, value);
        }

        public string ReadAddress
        {
            get => _readAddress;
            set => Set(ref _readAddress, value);
        }

        public string WriteAddress
        {
            get => _writeAddress;
            set => Set(ref _writeAddress, value);
        }

        // 命令属性
        public RelayCommand ConnectCommand { get; private set; }
        public RelayCommand DisconnectCommand { get; private set; }
        public RelayCommand ReadCommand { get; private set; }
        public RelayCommand WriteCommand { get; private set; }

        // 构造函数
        public MainViewModel()
        {
            // 初始化命令
            ConnectCommand = new RelayCommand(async () => await Connect());
            DisconnectCommand = new RelayCommand(async () => await Disconnect());
            ReadCommand = new RelayCommand(async () => await ReadFromPLC());
            WriteCommand = new RelayCommand(async () => await WriteToPLC());

            // 启动日志
            AddLog("应用程序启动");
        }

        /// <summary>
        /// 添加日志记录(时间戳 + 消息)
        /// </summary>
        /// <param name="message">日志内容</param>
        private void AddLog(string message)
        {
            Log = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}\n{Log}";
        }

        /// <summary>
        /// 连接PLC
        /// </summary>
        private async Task Connect()
        {
            try
            {
                AddLog($"正在连接PLC: {IpAddress}, 机架: {Rack}, 槽号: {Slot}");

                // 初始化PLC对象并连接(异步执行)
                _plc = new Plc(CpuType.S71200, IpAddress, Rack, Slot);
                await Task.Run(() => _plc.Open());

                // 更新连接状态
                IsConnected = _plc.IsConnected;
                ConnectionStatus = IsConnected ? "已连接" : "连接失败";
                AddLog(ConnectionStatus);
            }
            catch (Exception ex)
            {
                // 异常处理
                ConnectionStatus = "连接错误: " + ex.Message;
                IsConnected = false;
                AddLog(ConnectionStatus);
                MessageBox.Show(ex.Message, "连接错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// 断开PLC连接
        /// </summary>
        private async Task Disconnect()
        {
            try
            {
                if (_plc != null && _plc.IsConnected)
                {
                    AddLog("正在断开PLC连接");

                    // 异步断开连接
                    await Task.Run(() => _plc.Close());

                    // 更新状态
                    IsConnected = false;
                    ConnectionStatus = "已断开";
                    AddLog(ConnectionStatus);
                }
            }
            catch (Exception ex)
            {
                // 异常处理
                ConnectionStatus = "断开错误: " + ex.Message;
                AddLog(ConnectionStatus);
                MessageBox.Show(ex.Message, "断开错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// 从PLC读取数据
        /// </summary>
        private async Task ReadFromPLC()
        {
            try
            {
                if (_plc != null && _plc.IsConnected)
                {
                    AddLog($"正在读取PLC地址: {ReadAddress}");

                    // 异步读取数据
                    var value = await Task.Run(() => _plc.Read(ReadAddress));

                    // 更新读取结果
                    ReadValue = value?.ToString() ?? "读取失败";
                    AddLog($"读取成功: {ReadValue}");
                }
                else
                {
                    // 未连接状态提示
                    AddLog("读取失败: 请先连接PLC");
                    MessageBox.Show("请先连接PLC", "错误", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
            catch (Exception ex)
            {
                // 异常处理
                ReadValue = "读取错误: " + ex.Message;
                AddLog(ReadValue);
                MessageBox.Show(ex.Message, "读取错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// 向PLC写入数据
        /// </summary>
        private async Task WriteToPLC()
        {
            try
            {
                if (_plc != null && _plc.IsConnected)
                {
                    // 验证输入是否为有效数值
                    if (double.TryParse(WriteValue, out double numericValue))
                    {
                        AddLog($"正在写入PLC地址: {WriteAddress}, 值: {numericValue}");

                        // 异步写入数据
                        await Task.Run(() => _plc.Write(WriteAddress, numericValue));

                        // 写入成功提示
                        AddLog("写入成功");
                        MessageBox.Show("写入成功", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
                    }
                    else
                    {
                        // 输入格式错误提示
                        AddLog("写入失败: 请输入有效的数值");
                        MessageBox.Show("请输入有效的数值", "错误", MessageBoxButton.OK, MessageBoxImage.Warning);
                    }
                }
                else
                {
                    // 未连接状态提示
                    AddLog("写入失败: 请先连接PLC");
                    MessageBox.Show("请先连接PLC", "错误", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
            catch (Exception ex)
            {
                // 异常处理
                AddLog($"写入错误: {ex.Message}");
                MessageBox.Show(ex.Message, "写入错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    }
}
XML 复制代码
<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        xmlns:vm="clr-namespace:WpfApp1.ViewModel"
        mc:Ignorable="d"
        Title="西门子S7 PLC通信" Height="546.939" Width="816.764">
    <Window.DataContext>
        <vm:MainViewModel />
    </Window.DataContext>
    <Grid Margin="20">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <!-- 标题 -->
        <TextBlock Grid.Row="0" Grid.ColumnSpan="2" Text="西门子S7 PLC通信" FontSize="24" FontWeight="Bold" Margin="0,0,0,20" HorizontalAlignment="Center" />
        <!-- 连接设置 -->
        <GroupBox Grid.Row="1" Grid.ColumnSpan="2" Header="连接设置" Margin="0,0,0,20">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <!-- IP地址 -->
                <Label Grid.Row="0" Grid.Column="0" Content="IP地址:" Margin="10,10,10,10" VerticalAlignment="Center" />
                <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding IpAddress}" Margin="0,10,10,10" Height="30" />
                <!-- 机架号 -->
                <Label Grid.Row="0" Grid.Column="2" Content="机架:" Margin="10,10,10,10" VerticalAlignment="Center" />
                <TextBox Grid.Row="0" Grid.Column="3" Text="{Binding Rack}" Margin="0,10,10,10" Width="60" Height="30" />
                <!-- 槽号 -->
                <Label Grid.Row="0" Grid.Column="4" Content="槽号:" Margin="10,10,10,10" VerticalAlignment="Center" />
                <TextBox Grid.Row="0" Grid.Column="5" Text="{Binding Slot}" Margin="0,10,10,10" Width="60" Height="30" />
                <!-- 连接状态 -->
                <Label Grid.Row="1" Grid.Column="0" Content="连接状态:" Margin="10,10,10,10" VerticalAlignment="Center" />
                <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding ConnectionStatus}" Margin="0,10,10,10" Height="30" VerticalAlignment="Center" FontWeight="Bold" />
                <!-- 连接按钮 -->
                <Button Grid.Row="1" Grid.Column="2" Content="连接" Command="{Binding ConnectCommand}" Margin="0,10,10,10" Width="80" Height="30" IsEnabled="{Binding !IsConnected}" />
                <!-- 断开按钮 -->
                <Button Grid.Row="1" Grid.Column="3" Content="断开" Command="{Binding DisconnectCommand}" Margin="0,10,10,10" Width="80" Height="30" IsEnabled="{Binding IsConnected}" />
            </Grid>
        </GroupBox>
        <!-- 读取操作 -->
        <GroupBox Grid.Row="2" Grid.Column="0" Header="读取操作" Margin="0,0,10,20" Width="380">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <!-- 读取地址 -->
                <Label Grid.Row="0" Grid.Column="0" Content="读取地址:" Margin="10,10,10,10" VerticalAlignment="Center" />
                <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding ReadAddress}" Margin="0,10,10,10" Height="30" />
                <!-- 读取按钮 -->
                <Button Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Content="读取" Command="{Binding ReadCommand}" Margin="10,10,10,10" Height="40" IsEnabled="{Binding IsConnected}" />
                <!-- 读取结果 -->
                <Label Grid.Row="2" Grid.Column="0" Content="读取结果:" Margin="10,10,10,10" VerticalAlignment="Center" />
                <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding ReadValue}" Margin="0,10,10,10" Height="30" IsReadOnly="True" Background="#F0F0F0" />
            </Grid>
        </GroupBox>
        <!-- 写入操作 -->
        <GroupBox Grid.Row="2" Grid.Column="1" Header="写入操作" Margin="0,0,0,20" Width="380">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <!-- 写入地址 -->
                <Label Grid.Row="0" Grid.Column="0" Content="写入地址:" Margin="10,10,10,10" VerticalAlignment="Center" />
                <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding WriteAddress}" Margin="0,10,10,10" Height="30" />
                <!-- 写入数值 -->
                <Label Grid.Row="1" Grid.Column="0" Content="写入数值:" Margin="10,10,10,10" VerticalAlignment="Center" />
                <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding WriteValue}" Margin="0,10,10,10" Height="30" />
                <!-- 写入按钮 -->
                <Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Content="写入" Command="{Binding WriteCommand}" Margin="10,10,10,10" Height="40" IsEnabled="{Binding IsConnected}" />
            </Grid>
        </GroupBox>
        <!-- 状态日志 -->
        <GroupBox Grid.Row="3" Grid.ColumnSpan="2" Header="操作日志" Margin="0,0,0,0">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <TextBox Margin="10,10,10,10" IsReadOnly="True" Background="#F0F0F0" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Text="{Binding Log}" />
            </Grid>
        </GroupBox>
    </Grid>
</Window>
相关推荐
zhangrelay18 小时前
linux下如何通过与AI对话设置thinkpad电池充电阈值
linux·运维·笔记·学习
张祥64228890418 小时前
误差理论与测量平差基础笔记八
笔记·算法·机器学习
zhangrelay18 小时前
Isaac Sim的物质基础要求为啥样?
笔记·学习
easyboot19 小时前
C#使用pythonnet简单示例
开发语言·python·c#
航Hang*19 小时前
计算机等级考试(二级WPS)---第1章:综合应用基础---第3节:云办公云服务
笔记·学习·wps·复习·计算机二级·计算机等级考试
轴测君19 小时前
CBAM(Convolutional Block Attention Module)
人工智能·pytorch·笔记
爱学java的ptt20 小时前
万字亿集流量系统架构总结笔记(上)
笔记·系统架构
刘欣的博客20 小时前
c# winform 控件dock 停造位置、摆放顺序问题
c#·winform·dock停靠问题
Hyacinth&20 小时前
IntelliJ IDEA|学习笔记
笔记·学习·intellij-idea
求真求知的糖葫芦21 小时前
耦合传输线分析学习笔记(六)不对称耦合微带线Z参数矩阵推导与应用
笔记·学习·矩阵·射频工程