C# 使用channel 实现Plc 异步任务之间的通信

channel 通信的例子:

while情况下:

using System.Threading.Channels;

int i = 0;
var channel = Channel.CreateUnbounded<Message>();
//生产者1
var sender1 = SendMessageThreadAsync(channel.Writer, 1);
//生产者2
var sender2 = SendMessageThreadAsync(channel.Writer, 2);
//消费者
var receiver1 = ReceiveMessageThreadAsync(channel.Reader, 3);

await Task.WhenAll(sender1, sender2);

//如果生产者确定生产完成, 用 channel.Writer.Complete(); 标记完成

//channel.Writer.Complete();
await Task.WhenAll(receiver1);
//生产者
async Task SendMessageThreadAsync(ChannelWriter<Message> writer, int id)
{
    while (true)
    {
        i = i + 1;
        await writer.WriteAsync(new Message(id, i.ToString()));
        Console.WriteLine($"Thread {id} sent {i}");
        await Task.Delay(100);
    }
}
//消费者
async Task ReceiveMessageThreadAsync(ChannelReader<Message> reader, int id)
{
    await foreach (var message in reader.ReadAllAsync())
    {
        Console.WriteLine($"Thread {id} received {message.Content}");
    }
}
record Message(int FromId, string Content);


using ConsoleApp2;
using System.Collections.Concurrent;
using System.Threading.Channels;

var queue = new BlockingCollection<Message>(new ConcurrentQueue<Message>());

var opt = new BoundedChannelOptions(10)
{
    FullMode = BoundedChannelFullMode.Wait,
    SingleReader = true,
    SingleWriter = true,
    Capacity = 100 //最大容量
};

//有限的
var channelTest = Channel.CreateBounded<Message>(opt);


//无限的
var channel = Channel.CreateUnbounded<Message>();

var sender1 = SendMessageThreadAsync(channel.Writer, 1);
var sender2 = SendMessageThreadAsync(channel.Writer, 2);
var receiver1 = ReceiveMessageThreadAsync(channel.Reader, 3);
var receiver2 = ReceiveMessageThreadAsync(channel.Reader, 4);
//await sender;
// make sure all messages are received

await Task.WhenAll(sender1, sender2);

channel.Writer.Complete();

await Task.WhenAll(receiver1, receiver2);

//await receiver;

Console.WriteLine("Press any key to exit...");
Console.ReadKey();

async Task SendMessageThreadAsync(ChannelWriter<Message> writer, int id)
{
    for (int i = 0; i < 20; i++)
    {
        await writer.WriteAsync(new Message(id, i.ToString()));
        Console.WriteLine($"Thread {id} sent {i}");
        await Task.Delay(100);
    }
}

async Task ReceiveMessageThreadAsync(ChannelReader<Message> reader, int id)
{

    //try
    //{
    //    while (!reader.Completion.IsCompleted)
    //    {
    //        var message = await reader.ReadAsync();
    //        Console.WriteLine($"Thread {id} received {message.Content}");
    //    }
    //}
    //catch (Exception ex)
    //{
    //    Console.WriteLine($"Thread {id} channel closed:{ex.Message}");
    //}

    await foreach (var message in reader.ReadAllAsync())
    {
        Console.WriteLine($"Thread {id} received {message.Content}");
    }
}

record Message(int FromId, string Content);






改造为Plc的实例

record PlcDataMessage  
{  
    public bool IsConnected { get; init; }  
    public DbData DbData { get; init; }  
    // 可以添加其他需要传递的信息  
}
// 创建一个无边界的Channel来发送和接收消息  
var plcDataChannel = Channel.CreateUnbounded<PlcDataMessage>();  
  
// 启动一个新的任务来模拟PLC数据读取  
Task.Factory.StartNew(async () =>  
{  
    var cts = new CancellationTokenSource(); // 假设您已经有了取消令牌源  
    while (!cts.IsCancellationRequested)  
    {  
        try  
        {  
            // ... 省略了连接PLC的代码,这部分逻辑保持不变 ...  
  
            if (MyIsConnected)  
            {  
                DbData dbDataTemp = await s7Plc.ReadClassAsync<DbData>(42, 0);  
  
                // 心跳和其他操作...  
  
                // 构造消息并发送到Channel  
                var message = new PlcDataMessage  
                {  
                    IsConnected = MyIsConnected,  
                    DbData = dbDataTemp  
                };  
                await plcDataChannel.Writer.WriteAsync(message, cts.Token);  
            }  
  
            // ... 其他逻辑保持不变 ...  
        }  
        catch (Exception ex)  
        {  
            // 处理异常并重新连接PLC(如果需要)  
            // ...  
  
            // 可以通过Channel发送一个特殊的消息来表示连接已断开或发生了错误  
            // 这里省略了这部分逻辑  
  
            // 休眠一段时间后再重试  
            await Task.Delay(2000, cts.Token);  
        }  
    }  
  
    // 完成后通知Channel不再发送更多数据  
    plcDataChannel.Writer.Complete();  
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);  
  
// 在另一个任务或线程中读取Channel中的数据  
Task.Run(async () =>  
{  
    await foreach (var message in plcDataChannel.Reader.ReadAllAsync(cts.Token))  
    {  
        if (message.IsConnected)  
        {  
            lock (lockObj)  
            {  
                // 更新dbData,这里假设dbData是一个线程安全的对象或结构  
                dbData.Str_S = message.DbData.Str_S.Trim();  
                // ... 更新其他属性 ...  
            }  
            // 处理读取到的数据...  
        }  
        else  
        {  
            // 处理PLC断开连接的情况...  
        }  
    }  
  
    // 读取完成,Channel已关闭  
    Console.WriteLine("PLC数据读取完毕。");  
}, cts.Token);  
  
// ... 其他代码,如等待所有任务完成、处理取消逻辑等 ...
using System;  
using System.Threading;  
using System.Threading.Channels;  
using System.Threading.Tasks;  
  
// ... 其他必要的引用和类型定义 ...  
  
// 创建一个无边界的Channel来发送和接收消息  
var plcDataChannel = Channel.CreateUnbounded<PlcDataMessage>();  
  
// 取消令牌源  
var cts = new CancellationTokenSource();  
  
// 启动一个新的任务来模拟PLC数据读取  
Task.Run(async () =>  
{  
    Plc s7Plc = null;  
    bool MyIsConnected = false;  
    int errorTimes = 0;  
  
    try  
    {  
        while (!cts.IsCancellationRequested)  
        {  
            if (s7Plc == null || !MyIsConnected)  
            {  
                // 尝试连接PLC(略去具体实现)  
                // ...  
  
                if (MyIsConnected)  
                {  
                    // 连接成功,发送连接成功消息(如果需要)  
                    // ...  
                }  
            }  
            else  
            {  
                try  
                {  
                    // 读取PLC数据(略去具体实现)  
                    DbData dbDataTemp = await s7Plc.ReadClassAsync<DbData>(42, 0, cts.Token);  
  
                    // 心跳和其他操作...  
  
                    // 构造消息并发送到Channel  
                    var message = new PlcDataMessage { IsConnected = MyIsConnected, DbData = dbDataTemp };  
                    await plcDataChannel.Writer.WriteAsync(message, cts.Token);  
  
                    errorTimes = 0; // 重置错误计数器  
                }  
                catch (Exception ex)  
                {  
                    errorTimes++;  
                    // 处理异常(例如记录日志)  
                    // ...  
  
                    // 在达到一定错误次数后,关闭PLC连接并重置  
                    if (errorTimes > someThreshold)  
                    {  
                        s7Plc?.Close();  
                        s7Plc = null;  
                        MyIsConnected = false;  
                        // 可以选择发送一个断开连接的消息到Channel  
                    }  
  
                    // 休眠一段时间后再重试  
                    await Task.Delay(2000, cts.Token);  
                }  
            }  
  
            // 可以添加一些延时来减少循环的频率  
            await Task.Delay(somePollingInterval, cts.Token);  
        }  
    }  
    catch (OperationCanceledException)  
    {  
        // 取消是预期的,不需要额外处理  
    }  
    finally  
    {  
        // 确保关闭PLC连接和Channel写入器  
        s7Plc?.Close();  
        plcDataChannel.Writer.Complete();  
    }  
}, cts.Token);  
  
// 在另一个任务或线程中读取Channel中的数据  
Task.Run(async () =>  
{  
    await foreach (var message in plcDataChannel.Reader.ReadAllAsync(cts.Token))  
    {  
        if (message.IsConnected)  
        {  
            // 更新dbData(这里假设dbData是一个线程安全的对象或结构)  
            // 根据需要添加适当的同步机制  
            // ...  
  
            // 处理读取到的数据...  
        }  
        else  
        {  
            // 处理PLC断开连接的情况...  
        }  
    }  
  
    // 读取完成,Channel已关闭  
    Console.WriteLine("PLC数据读取完毕。");  
}, cts.Token);  
  
// ... 其他代码,如等待所有任务完成、处理取消逻辑等 ...  
  
// 在某个适当的时刻取消任务  
// cts.Cancel();  
  
// 等待所有任务完成(如果需要
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潘诺西亚的火山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值