Create a Blinky Blazor app in the blink of an eye.

在眨眼间构建您的 Blazor IoT 应用!

我想我的第一ASP.NET核心版”你好,布林基”将是我的最后一次,至少很长一段时间。但后来有些事情让我想起了布拉佐,我想为什么不建立一个Blazor版的Hello,闪烁的Windows物联网核心和树莓派?经过一些黑客攻击,我让它工作。这是我的你好,布拉佐的闪烁。

您可能还喜欢:
构建ASP.NET核心”你好,闪烁”物联网应用程序

赫克是谁你好,布林基?

你好,布林基有点像树莓派和其他微板的”你好,世界”。虽然可以使用这些电路板做一个”你好,世界”,为什么不做一些更有趣的互联电子产品呢?这难道不是这些板的制作原因吗?

你好,闪烁的是一个闪烁的LED灯。这是你能在树莓派上做的最简单的事情之一。有许多使用不同语言、工具和实用程序的 Hello、Blinky 的动手示例。这个是给布拉佐的

布线

当然,要将 LED 灯与您的树莓派一起使用,我们需要购买一盏。我们还需要一个面包板、电线和电阻器。只需访问您当地的电子商店,并询问他们这些组件。他们知道该卖给你什么

布线其实很容易。只需连接 LED、电阻和导线,如下图所示。

connecting the LED, resistor, and wires

NB!我使用树莓派与Windows 10物联网核心(有一个免费版本可用)。这个代码实际上也应该在Linux上工作,但我没有尝试过,我不知道发布是否以同样的方式为我们希望在Linux上托管的Blazor应用程序工作。

接线完成后,是时候开始编码了。

布拉佐解决方案

我决定使用服务器端 Web 应用程序支持的客户端 Blazor 应用程序。

以这种方式构建服务器端 Blazor 应用程序是可能的,但就实现而言,使用这一技巧会更容易。

服务器端 Blazor 处理服务器中的事件。因此,所有 UI 事件都在服务器中处理。在这种情况下,UI 和服务器使用 Web 套接字和 SignalR 进行通信。

这是我想避免的。树莓派是一个资源很少的小板——至少我拥有的不是很强大。我不想将 UI 工作负载放在那里,因为浏览器有更多的资源可以使用。

考虑到这一点,我创建了一个 Visual Studio 解决方案,该解决方案显示在右侧的图像中。BlazorHelloBlinky.Client 是一个客户端的 Blazor 应用程序,BlazorHelloBlinky.Server 是一个在树莓派上运行的 Web 应用程序。

从ASP.NET核心控制器闪烁 LED

在任何其他操作之前,我们需要一个类来闪烁 LED 灯。没有用于闪烁的编程概念。没有命令像 make-led-lamp-blink() 。因此,我们必须自己写它。

以下是闪烁周期对我们的计算机的意义:

  1. 向 GPIO 引脚发送信号
  2. 等待一秒钟
  3. 切断信号
  4. 等待一秒钟
  5. 继续步骤 1

问题是,我们不能闪烁LED,只有一个命令

为此,我写了 LedBlinkClient 类。它承载具有无限循环的任务,但在循环中,它会检查是否是时候停止。

下面是 LedBlinkClient 服务器端 Web 应用程序的类(它需要System.Device.Gpio Nuget 包才能工作)。

public class LedBlinkClient : IDisposable
{
    private const int LedPin = 17;
    private const int LightTimeInMilliseconds = 1000;
    private const int DimTimeInMilliseconds = 200;
 
    private bool disposedValue = false;
    private object _locker = new object();
    private bool _isBlinking = false;
 
    private Task _blinkTask;
    private CancellationTokenSource _tokenSource;
    private CancellationToken _token;
 
    public void StartBlinking()
    {
        if (_blinkTask != null)
        {
            return;
        }
 
        lock (_locker)
        {
            if (_blinkTask != null)
            {
                return;
            }
 
            _tokenSource = new CancellationTokenSource();
            _token = _tokenSource.Token;
 
            _blinkTask = new Task(() =>
            {
                using (var controller = new GpioController())
                {
                    controller.OpenPin(LedPin, PinMode.Output);
 
                    _isBlinking = true;
 
                    while (true)
                    {
                        if (_token.IsCancellationRequested)
                        {
                            break;
                        }
 
                        controller.Write(LedPin, PinValue.High);
                        Thread.Sleep(LightTimeInMilliseconds);
                        controller.Write(LedPin, PinValue.Low);
                        Thread.Sleep(DimTimeInMilliseconds);
                    }
 
                    _isBlinking = false;
                }
            });
            _blinkTask.Start();
        }
    }
 
    public void StopBlinking()
    {
        if (_blinkTask == null)
        {
            return;
        }
 
        lock (_locker)
        {
            if (_blinkTask == null)
            {
                return;
            }
 
            _tokenSource.Cancel();
            _blinkTask.Wait();
            _isBlinking = false;
 
            _tokenSource.Dispose();
            _blinkTask.Dispose();
 
            _tokenSource = null;
            _blinkTask = null;
        }
    }
 
    public bool IsBlinking
    {
        get { return _isBlinking; }
    }
 
    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                StopBlinking();
            }
 
            disposedValue = true;
        }
    }
 
    public void Dispose()
    {
        Dispose(true);
    }
}

服务器端应用程序还需要一个控制器,以便 Blazor 可以控制闪烁。下面是我创建的简单控制器。

[Route("api/[controller]")]
public class BlinkyController
{
    private readonly LedBlinkClient _blinkClient;
 
    public BlinkyController(LedBlinkClient blinkClient)
    {
        _blinkClient = blinkClient;
    }
 
    [HttpGet("[action]")]
    public bool IsBlinking()
    {
        return _blinkClient.IsBlinking;
    }
 
    [HttpGet("[action]")]
    public void StartBlinking()
    {
        _blinkClient.StartBlinking();
    }
 
    [HttpGet("[action]")]
    public void StopBlinking()
    {
        _blinkClient.StopBlinking();
    }
}

控制器操作只是 LED 客户端类的基于 HTTP 的公共终结点。

当然, LedBlinkClient 必须在类中注册, Startup 因为我们希望通过控制器和依赖项注入的构造函数获取它。

services.AddSingleton<LedBlinkClient>();

我将其注册为单例,因为我不想创建多个 LED 客户端实例com/blazor 代码背后/”rel=”nofollow”目标=”_blank”=Blazor 支持代码落后文件,将其命名为PageName.razor.cs)。下面是索引页的标记。

@page "/"
@inherits IndexPage
 
<h1>Hello, blinky!</h1>
 
<p>Led is @BlinkyStatus</p>
 
<div>
    <button class="btn btn-success" @onclick="@StartBlinking">Start blinking</button> | 
    <button class="btn btn-danger" @onclick="@StopBlinking">Stop blinking</button>
</div>

下面是索引页的类。我保留它作为一个代码落后的文件,所以我的Blazor页面不包含任何逻辑。

public class IndexPage : ComponentBase
{
    [Inject]
    public HttpClient HttpClient { get; set; }
 
    [Inject]
    public IJSRuntime JsRuntime { get; set; }
 
    public string BlinkyStatus;
 
    protected override async Task OnInitializedAsync()
    {
        var thisRef = DotNetObjectReference.Create(this);
 
        await JsRuntime.InvokeVoidAsync("blinkyFunctions.startBlinky", thisRef);
    }
 
    protected async Task StartBlinking()
    {
        await HttpClient.GetStringAsync("/api/Blinky/StartBlinking");
    }
 
    protected async Task StopBlinking()
    {
        await HttpClient.GetStringAsync("/api/Blinky/StopBlinking");
    }
 
    [JSInvokable]
    public async Task UpdateStatus()
    {
        var isBlinkingValue = await HttpClient.GetStringAsync("/api/Blinky/IsBlinking");
 
        if (string.IsNullOrEmpty(isBlinkingValue))
        {
            BlinkyStatus = "in unknown status";
        }
        else
        {
            bool.TryParse(isBlinkingValue, out var isBlinking);
            BlinkyStatus = isBlinking ? "blinking" : "not blinking";
        }
 
        StateHasChanged();
    }
}

需要注意的一点很重要,那就是 OnInitializedAsync() 方法。打开页面时调用此方法。它为 JavaScript 创建索引页引用,并启动 JavaScript 计时器以定期更新闪烁的状态。

自动更新闪烁状态

我无法让任何 C# 计时器在我的浏览器中工作,所以我去了JavaScript互操作。好老 setInterval()  与一些布拉佐技巧使事情的工作。下面的图片说明了我所做的技巧。

Blazor tricks

加载页面后,我使用 JavaScript 互操作将页面引用发送到 JavaScript。使用 方法创建计时器后,将保存 setInterval() 引用。每五秒,将触发计时器回调,并调用方法读取 LED 状态。是的,此方法在 Blazor 窗体中定义,并且从 JavaScript 调用,没有黑客攻击。

将 JavaScript 文件添加到客户端 Blazor 应用程序的 wwwroot 文件夹中,并将其包含在同一文件夹中的 index.html 文件中。以下是 JavaScript 文件的内容:

window.blinkyFunctions = {
    blazorForm: null,
    startBlinky: function (formInstance) {       
        window.blinkyFunctions.blazorForm = formInstance;
        setInterval(window.blinkyFunctions.updateStatus, 5000);
    },
    updateStatus: function () {
        if (window.blinkyFunctions.blazorForm == null) {
            return;
        }

        window.blinkyFunctions.blazorForm.invokeMethodAsync('UpdateStatus');
    }
}

startBlinky()是从 Blazor 页调用的方法。updateSatus()计时器每五秒调用一次该方法。在计时器回调方法中,有一个 invokeMethodAsync() 调用。这就是我们在JavaScript中调用Blazor对象的方法的方法。

为什么不更新 JavaScript 中的状态?嗯,因为这篇文章是关于Blazor的,而且我想使用Blazor来构建,这也是一个很好的临时解决方案,对于需要计时器的Balzor应用程序

有几个技巧,我不得不找出硬的方式,并节省你的时间,我将在这里描述我做了什么。

第一件事是程序集。如果存在版本降级,则将其作为错误处理。对于一些人来说,当他们将带有降级版本的 Nuget 包添加到他们的解决方案时,它很有效,但对我来说却行不通。通过修改 Web 应用程序的项目文件,我们可以忽略项目级别的警告 NU1605。

<PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <LangVersion>7.3</LangVersion>
    <OutputType>Exe</OutputType>
    <NoWarn>$(NoWarn);NU1605</NoWarn>
  </PropertyGroup>

NoWarn标记告诉工具,此警告不能作为错误处理。

在我的树莓派上,我想用端口5000做我眨眼的应用程序。为了在最小的工作量内实现它,我添加了端口绑定到 Web 应用程序的Program.cs文件。

现在是时候构建整个解决方案,然后专注于真正的发布。

我知道,我知道,在LED开始闪烁之前,最后也是最烦人的步骤…它也被称为玩非稳定技术的乐趣。因此,以下是发布过程中最脏的部分:

  1. 使用可视化工作室发布客户端 Blazor 应用程序。保留所有设置,就像所有设置一样,只要使用默认值就一样做好工作
  2. 使用以下命令在命令行上发布服务器端 Blazor应用程序:dotnet 发布 -c 发布 -r win10 臂 /p:发布单一文件_true
  3. 打开 C:*驱动器的树莓和创建文件夹闪烁
  4. 将文件从 BlazorHelloBlinky.Server_bin_释放_netcoreapp3.0_win10-arm_复制到树莓派上的闪烁文件夹。
  5. 打开文件 BlazorHelloBlinky.Client.blazor.config,使其看起来像这里:c:\Blinky_BlazorHelloBlinky.Client.csproj
    布拉佐尔·赫林基.Client.dll
  6. 将客户端 Blazor 应用程序的 wwwroot 文件夹复制到树莓派上的闪烁文件夹
  7. 从将客户端大小的 Blazor 应用程序发布到树莓派上的 Blinky 文件夹的文件夹中复制 dist 文件夹
  8. 使用 PowerShell 连接到树莓派
  9. 移动到闪烁文件夹并运行 BlazorHelloBlinky.Server.exe
  10. 打开编码框上的浏览器并导航到http://minwinpc:5000/(使用树莓派的名称或 IP)

如果没有问题,所有必需的文件得到了树莓派,然后你好,闪烁应该准备采取行动!

进一步阅读

构建ASP.NET核心”你好,闪烁”物联网应用程序

教程:与恩智浦LPC845-BRK板闪烁

Comments are closed.