secs_learn/Program.cs
此文件是应用程序的入口点,用于配置和启动主机。
- 使用
Host.CreateDefaultBuilder(args)
创建一个默认配置的主机构建器,自动加载环境变量、配置文件等设置。 .ConfigureServices(...)
方法中,通过services.AddHostedService<DeviceWorker>()
注册DeviceWorker
类型作为托管服务,这样在主机启动时会自动实例化并运行这个服务。- 最后,
.Build().Run();
构建并启动主机,执行注册的服务(在这里即为DeviceWorker
)。整个程序将持续运行,直到被显式停止或遇到不可恢复的错误。
using DeviceWorkerService;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<DeviceWorker>();
}).Build().Run();
secs_learn/DeviceWorker.cs
这个文件中定义了一个名为 DeviceWorker
的类,该类继承自 BackgroundService
类,这是 .NET Core 中用于创建长时间运行后台服务的基类。
ILogger<DeviceWorker>
:注入日志记录器,用于在服务执行过程中输出日志信息。ISecsConnection _hsmsConnection
:注入一个实现了ISecsConnection
接口的对象,它代表了与半导体设备之间的HSMS(High-Speed SECS Message Services)连接。ISecsGem _secsGem
:注入一个实现了ISecsGem
接口的对象,用于提供与遵循GEM规范的设备进行交互的方法。
在构造函数中,设置了 _hsmsConnection.ConnectionChanged
事件处理器,以便当连接状态改变时记录日志信息。
ExecuteAsync
方法是 BackgroundService
的核心方法,在服务启动后会被调用并持续执行,直到收到取消信号或者发生异常。在此方法内:
- 启动HSMS连接。
- 使用异步循环等待接收来自设备的主消息(Primary Message)。
- 对每一个接收到的主消息,构建对应的次消息(Secondary Message)并尝试回复给设备,如果在这个过程中出现异常,则记录错误日志。
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Secs4Net;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace DeviceWorkerService;
internal sealed class DeviceWorker : BackgroundService
{
private readonly ILogger<DeviceWorker> _logger;
private readonly ISecsConnection _hsmsConnection;
private readonly ISecsGem _secsGem;
public DeviceWorker(ILogger<DeviceWorker> logger, ISecsConnection hsmsConnection, ISecsGem secsGem)
{
_logger = logger;
_hsmsConnection = hsmsConnection;
_secsGem = secsGem;
_hsmsConnection.ConnectionChanged += delegate
{
switch (_hsmsConnection.State)
{
case ConnectionState.Retry:
_logger.LogError($"Connection loss, try to reconnect.");
break;
case ConnectionState.Connecting:
case ConnectionState.Connected:
_logger.LogWarning(_hsmsConnection.State.ToString());
break;
default:
_logger.LogInformation($"Connection state = {_hsmsConnection.State}");
break;
}
};
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
_hsmsConnection.Start(stoppingToken);
await foreach (var e in _secsGem.GetPrimaryMessageAsync(stoppingToken))
{
using var primaryMessage = e.PrimaryMessage;
_logger.LogInformation($"Received primary message: {primaryMessage}");
try
{
using var secondaryMessage = new SecsMessage(primaryMessage.S, (byte)(primaryMessage.F + 1))
{
SecsItem = primaryMessage.SecsItem,
};
await e.TryReplyAsync(secondaryMessage, stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occurred when processing primary message");
}
}
}
catch (Exception ex)
{
if (stoppingToken.IsCancellationRequested)
{
return;
}
_logger.LogError(ex, "Unhandled exception occurred on primary messages processing");
}
}
}