C#産業オートメーション開発完全ガイド:エンタープライズアプリケーションの構築

freeFree Technical Resource

This content is free to read, suitable for basic learning and search traffic.

C#産業オートメーション開発完全ガイド:エンタープライズアプリケーションの構築

C#/. NET産業オートメーション開発完全ガイド:エンタープライズクラスの産業用アプリケーションの構築

産業オートメーションにおけるC#/. NETの利点

C#/. NETプラットフォームは、強力な型システム、豊富なクラスライブラリ、優れたパフォーマンス、マイクロソフトの包括的なサポートにより、エンタープライズクラスの産業オートメーションの世界で重要な地位を占めています。従来のWindowsデスクトップアプリケーションから最新のクラウドネイティブマイクロサービスまで、C#/. NETは完全なソリューションスタックを提供し、特に高信頼性、高性能、長期メンテナンスを必要とする産業用アプリケーションに最適です。 この記事では、NModbusライブラリの使用、WPF/Win Formsインターフェイスの開発、エンタープライズアーキテクチャの設計、プロフェッショナルグレードの産業オートメーションシステムの構築方法を示す実践的なケーススタディに焦点を当て、産業オートメーションにおけるC#/. NETの使用について深く掘り下げます。

C#/. NET産業開発環境の設定

1.1開発環境の要件

<!-- . NET SDKのバージョン要件->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroupの略。>
    <TargetFramework Net 8.0/TargetFramework>
    <OutputType Exe/OutputType>
    <Nullable/Nullable>
  </PropertyGroup>
<プロジェクト/Project>

1.2プロジェクト構造と依存構成

<!-- IndustrialAutomation.csproj-->
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroupの略。>
    <WinExe/OutputTypeの設定>
    <TargetFramework net 8.0-Windows/TargetFramework>
    <UseWPF true/UseWPF>
    <Nullable/Nullable>
  </PropertyGroup>

  <ItemGroup>
    <!-- ModbusCommunication ->
    <PackageReference Include="NModbus" Version="3.0.73" />
    <PackageReference Include="NModbus.Serial" Version="3.0.73" />

    <!--シリアルCommunication-->
    <PackageReference Include="System.IO.Ports" Version="8.0.0" />

    <!--ログ--->
    <PackageReference Include="Serilog"バージョン="3.1.1" />
    <PackageReference Include="Serilog. Sinks.File" Version="5.0.0" />
    <PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />

    <!--依存性注入 ->
    <PackageReference Include="Microsoft. Extension.DependencyInjection" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />

    <!--構成管理 ->
    <PackageReference Include="Microsoft. Extension.Configuration" Version="8.0.0" />
    <PackageIn =". Extons.ation.Json" Version="8.0.0" />

    <!--データ検証->
    <PackageReference Include="FluentValidation" Version="11.8.0" />

    <!--ユニットテスト-->
    <PackageReference Include="xunit" Version="2.6.3" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.5" />
    <PackageReference Include="Moq" Version="4.20.69" />
  </ItemGroup>

</Project>

1.3プロジェクトのディレクトリ構造

産業オートメーション/
略称はSRC。
│ │ │   カテゴリー:Industrial Automation.Core/          コアビジネスロジック
│ │ │   │ │ │   あなたは-モデル。                         #データモデル
│ │ │   │ │ │   あなた-サービス/                       #ビジネスサービス
│ │ │   │ │ │   略称は“インターフェイス”。                     #インターフェースの定義
│ │ │   │ │ │   3-例外とは                     #例外の定義
│ │ │   │ │ │
│ │ │   Industrial Automation.Modbusの略。        # ModbusCommunicationモジュール
│ │ │   │ │ │   クライアント-クライアント。                        # Modbusクライアント
│ │ │   │ │ │   あなたはサーバーです。                        # Modbusサーバ
│ │ │   │ │ │   そして、Converters/                     #データコンバータ
│ │ │   │ │ │
│ │ │   カテゴリー:Industrial Automation.UI            #ユーザーインターフェース
│ │ │   │ │ │   あなたの意見-ビュー                          # WPFビュー
│ │ │   │ │ │   タイトル:ViewModels                     # MVVMビューモデル
│ │ │   │ │ │   あなたはConvertersです。                     #値変換器
│ │ │   │ │ │   10.“Be haviors”                      #振る舞いは
│ │ │   │ │ │
│ │ │   6--Industrial Automation.Host/          #アプリケーションホスト
│ │ │
あなた-テスト/テスト
│ │ │   カテゴリー:Industrial Automation.Core.Tests
│ │ │   カテゴリー:Industrial Automation.Modbus.Tests
│ │ │   産業オートメーション-統合-テスト/
│ │ │
>> config/とは
│ │ │   >> appsettings.json                    #設定の適用
│ │ │   appsettings.development.json        #開発環境の設定
│ │ │   >> devices.json                        #装備の配置
│ │ │
それは...ドキュメント。                                   #プロジェクトファイル

2. NModbusライブラリの深い使用

2.1 Modbus TCPクライアント

NModbusの使用
NModbus.Deviceを使用する
System.Net.Socketsの使用

名前空間産業オートメーション.Modbus.Clients
{
    //summary>
    // Modbus TCPクライアント
    ////summary>
    public class ModbusTcpClient IModbusClient、IDisposable
    {
        private readonly ILogger ModbusTcpclient_logger;
        private readonly Tcpclient_tcpClient;
        ate modbusmaster;
        private bool_disposed;

        //summary>
        ///デバイス接続ステータス
        ////summary>
        public bool IsConnected =_tcpClient. Connected()偽である。

        //summary>
        ///デバイスのIPアドレス
        ////summary>
        public string IpAddress { get; }

        //summary>
        //デバイスのポート
        ////summary>
        public int Port { get; }

        //summary>
        Modbus TCPクライアントの初期化
        ////summary>
        public ModbusTcpClient
            string ipAddress、
            int port = 502
            ILogger ModbusTcpClient logger = null
        {
            IpAddress = ipAddress throw new ArgumentNullException nameof ipAddress;
            ポート=ポート;
            _logger = logger NullLogger ModbusTcpClient.Instance;
            _tcpClient = new TcpClient();
        }

        /// <summary>
        /// 连接到设备
        /// </summary>
        public async Task<bool> ConnectAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                _logger.LogInformation("正在连接到设备 {IpAddress}:{Port}", IpAddress, Port);

                await _tcpClient.ConnectAsync(IpAddress, Port, cancellationToken);

                var factory = new ModbusFactory();
                _modbusMaster = factory.CreateMaster(_tcpClient);

                // 设置超时时间
                _modbusMaster.Transport.ReadTimeout = 3000;
                _modbusMaster.Transport.WriteTimeout = 3000;

                _logger.LogInformation("成功连接到设备 {IpAddress}:{Port}", IpAddress, Port);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "连接设备 {IpAddress}:{Port} 失败", IpAddress, Port);
                return false;
            }
        }

        /// <summary>
        /// Read and hold register
        /// </summary>
        public async Task<ushort[]> ReadHoldingRegistersAsync(
            byte slaveAddress,
            ushort startAddress,
            ushort numberOfPoints,
            CancellationToken cancellationToken = default)
        {
            ValidateConnection();

            try
            {
                _logger.LogDebug(
                    "Read and hold register - slave: {SlaveAddress}, address: {StartAddress}, Quantity: {NumberOfPoints}",
                    slaveAddress, startAddress, numberOfPoints);

                var result = await Task.Run(() =>
                    _modbusMaster!.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints),
                    cancellationToken);

                _logger.LogDebug(
                    "读取保持寄存器成功 - slave: {SlaveAddress}, address: {StartAddress}, data: {Data}",
                    slaveAddress, startAddress, result);

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,
                    "读取保持寄存器失败 - slave: {SlaveAddress}, address: {StartAddress}, Quantity: {NumberOfPoints}",
                    slaveAddress, startAddress, numberOfPoints);
                throw new ModbusCommunicationException(
                    $"读取保持寄存器失败: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// 写入单个保持寄存器
        /// </summary>
        public async Task WriteSingleRegisterAsync(
            byte slaveAddress,
            ushort registerAddress,
            ushort value,
            CancellationToken cancellationToken = default)
        {
            ValidateConnection();

            try
            {
                _logger.LogDebug(
                    "Write to a single register - slave: {SlaveAddress}, address: {RegisterAddress}, 值: {Value}",
                    slaveAddress, registerAddress, value);

                await Task.Run(() =>
                    _modbusMaster!.WriteSingleRegister(slaveAddress, registerAddress, value),
                    cancellationToken);

                _logger.LogDebug(
                    "写入单个寄存器成功 - slave: {SlaveAddress}, address: {RegisterAddress}",
                    slaveAddress, registerAddress);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,
                    "写入单个寄存器失败 - slave: {SlaveAddress}, address: {RegisterAddress}, 值: {Value}",
                    slaveAddress, registerAddress, value);
                throw new ModbusCommunicationException(
                    $"写入单个寄存器失败: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// 写入多个保持寄存器
        /// </summary>
        public async Task WriteMultipleRegistersAsync(
            byte slaveAddress,
            ushort startAddress,
            ushort[] data,
            CancellationToken cancellationToken = default)
        {
            ValidateConnection();

            try
            {
                _logger.LogDebug(
                    "Write multiple registers - slave: {SlaveAddress}, address: {StartAddress}, 数据长度: {Length}",
                    slaveAddress, startAddress, data.Length);

                await Task.Run(() =>
                    _modbusMaster!.WriteMultipleRegisters(slaveAddress, startAddress, data),
                    cancellationToken);

                _logger.LogDebug(
                    "写入多个寄存器成功 - slave: {SlaveAddress}, address: {StartAddress}",
                    slaveAddress, startAddress);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,
                    "写入多个寄存器失败 - slave: {SlaveAddress}, address: {StartAddress}, 数据长度: {Length}",
                    slaveAddress, startAddress, data.Length);
                throw new ModbusCommunicationException(
                    $"写入多个寄存器失败: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// Read coil status
        /// </summary>
        public async Task<bool[]> ReadCoilsAsync(
            byte slaveAddress,
            ushort startAddress,
            ushort numberOfPoints,
            CancellationToken cancellationToken = default)
        {
            ValidateConnection();

            try
            {
                _logger.LogDebug(
                    "读取线圈 - slave: {SlaveAddress}, address: {StartAddress}, Quantity: {NumberOfPoints}",
                    slaveAddress, startAddress, numberOfPoints);

                var result = await Task.Run(() =>
                    _modbusMaster!.ReadCoils(slaveAddress, startAddress, numberOfPoints),
                    cancellationToken);

                _logger.LogDebug(
                    "读取线圈成功 - slave: {SlaveAddress}, address: {StartAddress}, data: {Data}",
                    slaveAddress, startAddress, result);

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,
                    "读取线圈失败 - slave: {SlaveAddress}, address: {StartAddress}, Quantity: {NumberOfPoints}",
                    slaveAddress, startAddress, numberOfPoints);
                throw new ModbusCommunicationException(
                    $"读取线圈失败: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// 批量读取优化
        /// </summary>
        public async Task<Dictionary<ushort, ushort[]>> BatchReadRegistersAsync(
            byte slaveAddress,
            IEnumerable<RegisterReadRequest> requests,
            CancellationToken cancellationToken = default)
        {
            ValidateConnection();

            var results = new Dictionary<ushort, ushort[]>();

            foreach (var request in requests)
            {
                try
                {
                    var data = await ReadHoldingRegistersAsync(
                        slaveAddress,
                        request.StartAddress,
                        request.NumberOfPoints,
                        cancellationToken);

                    results[request.StartAddress] = data;
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex,
                        "批量读取失败 - address: {StartAddress}, Quantity: {NumberOfPoints}",
                        request.StartAddress, request.NumberOfPoints);
                    results[request.StartAddress] = Array.Empty<ushort>();
                }
            }

            return results;
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            try
            {
                _modbusMaster?.Dispose();
                _modbusMaster = null;

                if (_tcpClient.Connected)
                {
                    _tcpClient.Close();
                }

                _logger.LogInformation("已断开与设备 {IpAddress}:{Port} 的连接", IpAddress, Port);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "断开连接时发生错误");
            }
        }

        /// <summary>
        /// 验证连接状态
        /// </summary>
        private void ValidateConnection()
        {
            if (!IsConnected || _modbusMaster == null)
            {
                throw new InvalidOperationException("设备未连接");
            }
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize this;
        }

        protected virtual void Dispose bool disposing
        {
            if!_ disposed
            {
                if disposing
                {
                    Disconnect Disconnect nParticle nParticle;
                    _tcpClient.Dispose;
                }

                disposed = true;
            }
        }
    }

    //summary>
    ///レジスタ読み取り要求
    ////summary>
    public record RegisterReadRequest
        ushort StartAddress、
        ushort NumberOfPoints;
}

2.2 Modbus RTUクライアント

```csharp using NModbus; using NModbus.Serial; using System.IO.Ports; namespace IndustrialAutomation.Modbus.Clients { ///// Modbus RTUクライアント ///public class ModbusRtuClient : IModbusClient, IDisposable { private readonly ILogger_logger; private readonly SerialPort _serialPort; private IModbusMaster? _modbusMaster; private bool _disposed;
//summary>
    ///シリアル接続ステータス
    ////summary>
    public bool IsConnected =_serialPort. IsOpen偽である。

    //summary>
    ///シリアル名
    ////summary>
    public string PortName { get; }

    //summary>
    ///ボーレート
    ////summary>
    public int baud rate {}

    //summary>
    Modbus RTUクライアントの初期化
    ////summary>
    public ModbusRClient
        string portName、
        baudRate = 96 00です。
        ILogger ModbusRtuClient logger = null
    {
        portName = portName throw new ArgumentNullException nameof portName;
        BaudRate = baudRate;
        _logger = logger NullLogger ModbusRtuClient.Instance;

        _serialPort = new SerialPort portName
        {
            BaudRate = baudRate
            DataBits = 8、
            Parity = Parity.
            StopBits = StopBits.One
            ReadTimeout = 1000
            WriteTimeout = 1000
        };
    }

    //summary>
    ///シリアルデバイスに接続
    ////summary>
    public async bool Async ellationcancellation =
    {
        try tryより
        {
            _logger.LogInformation "シリアルポート{PortName}ボーレート{BaudRate}" PortName BaudRate;

            await Task.Run =>
            {
                if!_ serialPort.IsOpen
                {
                    _serialPort.Open Open nParticle nParticle;
                }
            } cancellationToken;

            var factory = new ModbusFactory;
            var adapter = new SerialPortAdapter_serialPort;
            _modbusMaster = factory. RMaster adapter;

            //Modbusパラメータの設定
            _modbusMaster.Transport.ReadTimeout = 1000;
            _modbusMaster.Transport.WriteTimeout = 1000;
            _modbusMaster.Transport.Retries = 3;
            _modbusMaster.Transport.WaitToRetryMilliseconds = 100;

            _logger.LogInformation "シリアルポート{PortName}が正常にオープンされました" PortName;
            真の帰還;
        }
        catch Exception ex
        {
            _logger.LogError ex "シリアルポート{PortName}のオープンに失敗しました" PortName;
            偽を返す。
        }
    }

    //summary>
    ///シリアルデバイスのスキャン
    ////summary>
    public async List byte DevicesAsync
        byte startAddress = 1
        byte endAddress = 247、
        ellation
    {
        var found = new List byte;

        _logger.LogInformation "スキャンデバイスアドレス范囲{StartAddress}-{EndAddress}" Address endAddress;

        for byte slAddress = Address; slAddress = endAddress; slAddress ++
        {
            if cancellationToken.IsCancellationRequested
            {
                休憩を取る。
            }

            try tryより
            {
                //デバイスIDの読み取りを試みる
                var result = await Task.Run(() =>
                    _modbusMaster!.ReadDeviceIdentification(slaveAddress, 0x01),
                    cancellationToken);

                if (result != null)
                {
                    foundDevices.Add(slaveAddress);
                    _logger.LogInformation("
Put this resource to use in a real project?

Go to the Tool Center for message parsing, CRC verification and device debugging, or submit your requirements for selection and integration advice.

Engineer Membership

Turn this article into actionable debugging resources

After activation, you can use advanced message parsing, resource pack downloads, code examples, engineering cases and priority technical support, suitable for real project delivery.

Unlimited Advanced Tools
Resource & Code Packs
Complete Engineering Case Library
Priority Technical Support

Leave a Reply

Your email address will not be published. Required fields are marked *.