简体   繁体   English

Windows IoT后台任务异步/等待

[英]Windows IoT BackgroundTask Async/Await

I am having a play with a Raspberry Pi 3 running Windows IoT. 我正在玩运行Windows IoT的Raspberry Pi 3。 I have wired up a DS18B20 sensor and I am able to communicate to it fine via a UWP app. 我已经连接好DS18B20传感器,并且可以通过UWP应用程序与之通信。

I now wanted to make this app into a BackgroundTask app. 我现在想将此应用程序变成BackgroundTask应用程序。 I am using this code for the OneWire coms 我将此代码用于OneWire coms

class WireSearchResult
{
    public byte[] id = new byte[8];
    public int lastForkPoint = 0;
}
public class OneWire
{
    private SerialDevice serialPort = null;
    DataWriter dataWriteObject = null;
    DataReader dataReaderObject = null;

    public async Task<string> GetFirstSerialPort()
    {
        try
        {
            string aqs = SerialDevice.GetDeviceSelector("UART0");
            var dis = await DeviceInformation.FindAllAsync(aqs);
            if (dis.Count > 0)
            {
                var deviceInfo = dis.First();
                return deviceInfo.Id;
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine("Unable to get serial device: " + ex.Message);
        }

        return null;
    }

    public void shutdown()
    {
        if (serialPort != null)
        {
            serialPort.Dispose();
            serialPort = null;
        }
    }

    async Task<bool> onewireReset(string deviceId)
    {
        try
        {
            if (serialPort != null)
                serialPort.Dispose();

            serialPort = await SerialDevice.FromIdAsync(deviceId);

            // Configure serial settings
            serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.BaudRate = 9600;
            serialPort.Parity = SerialParity.None;
            serialPort.StopBits = SerialStopBitCount.One;
            serialPort.DataBits = 8;
            serialPort.Handshake = SerialHandshake.None;

            dataWriteObject = new DataWriter(serialPort.OutputStream);
            dataWriteObject.WriteByte(0xF0);
            await dataWriteObject.StoreAsync();

            dataReaderObject = new DataReader(serialPort.InputStream);
            await dataReaderObject.LoadAsync(1);
            byte resp = dataReaderObject.ReadByte();
            if (resp == 0xFF)
            {
                System.Diagnostics.Debug.WriteLine("Nothing connected to UART");
                return false;
            }
            else if (resp == 0xF0)
            {
                System.Diagnostics.Debug.WriteLine("No 1-wire devices are present");
                return false;
            }
            else
            {
                //System.Diagnostics.Debug.WriteLine("Response: " + resp);
                serialPort.Dispose();
                serialPort = await SerialDevice.FromIdAsync(deviceId);

                // Configure serial settings
                serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
                serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
                serialPort.BaudRate = 115200;
                serialPort.Parity = SerialParity.None;
                serialPort.StopBits = SerialStopBitCount.One;
                serialPort.DataBits = 8;
                serialPort.Handshake = SerialHandshake.None;
                dataWriteObject = new DataWriter(serialPort.OutputStream);
                dataReaderObject = new DataReader(serialPort.InputStream);
                return true;
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine("Exception: " + ex.Message);
            return false;
        }
    }

    public async Task onewireWriteByte(byte b)
    {
        for (byte i = 0; i < 8; i++, b = (byte)(b >> 1))
        {
            // Run through the bits in the byte, extracting the
            // LSB (bit 0) and sending it to the bus
            await onewireBit((byte)(b & 0x01));
        }
    }

    async Task<byte> onewireBit(byte b)
    {
        var bit = b > 0 ? 0xFF : 0x00;
        dataWriteObject.WriteByte((byte)bit);
        await dataWriteObject.StoreAsync();
        await dataReaderObject.LoadAsync(1);
        var data = dataReaderObject.ReadByte();
        return (byte)(data & 0xFF);
    }

    async Task<byte> onewireReadByte()
    {
        byte b = 0;
        for (byte i = 0; i < 8; i++)
        {
            // Build up byte bit by bit, LSB first
            b = (byte)((b >> 1) + 0x80 * await onewireBit(1));
        }
       // System.Diagnostics.Debug.WriteLine("onewireReadByte result: " + b);
        return b;
    }

    public async Task<double> getTemperature(string deviceId)
    {
        double tempCelsius = -200;

        if (await onewireReset(deviceId))
        {
            await onewireWriteByte(0xCC); //1-Wire SKIP ROM command (ignore device id)
            await onewireWriteByte(0x44); //DS18B20 convert T command 
                                          // (initiate single temperature conversion)
                                          // thermal data is stored in 2-byte temperature 
                                          // register in scratchpad memory

            // Wait for at least 750ms for data to be collated
            await Task.Delay(750);

            // Get the data
            await onewireReset(deviceId);
            await onewireWriteByte(0xCC); //1-Wire Skip ROM command (ignore device id)
            await onewireWriteByte(0xBE); //DS18B20 read scratchpad command
                                          // DS18B20 will transmit 9 bytes to master (us)
                                          // starting with the LSB

            byte tempLSB = await onewireReadByte(); //read lsb
            byte tempMSB = await onewireReadByte(); //read msb

            // Reset bus to stop sensor sending unwanted data
            await onewireReset(deviceId);

            // Log the Celsius temperature
            tempCelsius = ((tempMSB * 256) + tempLSB) / 16.0;
            var temp2 = ((tempMSB << 8) + tempLSB) * 0.0625; //just another way of calculating it

            System.Diagnostics.Debug.WriteLine("Temperature: " + tempCelsius + " degrees C " + temp2);
        }
        return tempCelsius;
    }
}

And finally the StartupTask 最后是StartupTask

public sealed class StartupTask : IBackgroundTask
{
    private BackgroundTaskDeferral deferral;

    private OneWire onewire;
    private string deviceId = string.Empty;
    private bool inprog = false;
    private Timer timer;

    public void Run(IBackgroundTaskInstance taskInstance)
    {
        deferral = taskInstance.GetDeferral(); 

        onewire = new OneWire();
        deviceId = await onewire.GetFirstSerialPort();

        if(deviceId != null)
          await onewire.getTemperature(deviceId));

        BackgroundTaskDeferral.Complete();
    }

}

The problem I have is that when I run this code it hangs on one of the lines that disposes of the SerialDevice in OneWire class. 我的问题是,当我运行这段代码就挂在那部署的线路之一SerialDeviceOneWire类。

I have read in a few places that its related to the BackgroundTask and using Async/Await 我已经在一些地方阅读了与BackgroundTask相关的内容,并使用了Async / Await

This issue is not related to the BackgroundTask. 此问题与BackgroundTask无关。 Because your code produces the same issue in non BackgroundTask(app). 因为您的代码在非BackgroundTask(app)中会产生相同的问题。

Its reason looks like SerialPort is somewhat prone to deadlock . 其原因似乎是SerialPort有点容易死锁

I found there are too many callings of onewireReset method that close and reopen the SerialPort. 我发现关闭并重新打开SerialPort的onewireReset方法调用过多。 I don´t know why should this be done but this cause the problem. 我不知道为什么要这样做,但这会引起问题。

So there is a workaround: Rewrite the related part logic and make sure open the SerialPort at the start of your program and dispose it when you don't need it any more. 因此,有一个解决方法:重写相关的零件逻辑,并确保在程序开始时打开SerialPort,并在不再需要它时将其处置。

I'm using the same onewire code in a background task to talk to the DS18B20 sensor and was experiencing exactly the same behaviour as you. 我在后台任务中使用相同的onewire代码与DS18B20传感器通信,并且遇到与您完全相同的行为。

What I found is that if I put a delay of 100 milliseconds before calling the serial port dispose method it works 我发现的是,如果在调用串行端口处理方法之前放置了100毫秒的延迟,则它可以工作

await Task.Delay(100)

I've tried less than 100 milliseconds but it just kept hanging. 我尝试了不到100毫秒,但它一直挂着。

This stackoverflow questions first answer explains the issue with serial ports in the .Net Framework Why Thread.Sleep() before SerialPort.Open and Close? 这个stackoverflow问题的第一个答案解释了.Net Framework中串行端口的问题, 为什么在SerialPort.Open和Close之前使用Thread.Sleep()?

I have just been through the exact same problem myself yesterday (program suddenly hangs when disposing the SerialDevice object in the onewireReset(...) method) and I managed to resolve it. 昨天我自己经历了一个完全相同的问题(将代码转换为onewireReset(...)方法中的SerialDevice对象时,程序突然挂起),我设法解决了这个问题。

Solution principle: Don't constantly dispose/reaquire the serial port. 解决方案原则: 不要不停地处理/获取串行端口。 Instead, aquire the port once and reconfigure it on the fly as needed (=change the baud rate). 取而代之的是, 一次获取端口并根据需要动态配置它 (=更改波特率)。 This way the hanging SerialDevice.Dispose() call is avoided completely. 这样,完全避免了挂起的SerialDevice.Dispose()调用。

Note: In order to be able to change the baud rate, you must first detatch both the DataReader and DataWriter objects from the port stream, or you will get an exception. 注意:为了能够更改波特率,必须首先从端口流中分离DataReaderDataWriter对象,否则将获得异常。 After the change, reattach new DataReader and DataWriter objects (remember to properly dispose of the old ones). 更改后,重新附加新的DataReaderDataWriter对象(请记住要正确处理旧的对象)。

Modified OneWire class: 修改的OneWire类:

    public class OneWire
{
    private SerialDevice serialPort = null;
    DataWriter dataWriteObject = null;
    DataReader dataReaderObject = null;

    public void shutdown()
    {
        if (serialPort != null)
        {
            serialPort.Dispose();
            serialPort = null;
        }
    }

    private async Task ReconfigurePort(uint baudRate, string deviceId)
    {
        if (serialPort == null)
        {
            serialPort = await SerialDevice.FromIdAsync(deviceId);
            serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.BaudRate = baudRate;
            serialPort.Parity = SerialParity.None;
            serialPort.StopBits = SerialStopBitCount.One;
            serialPort.DataBits = 8;
            serialPort.Handshake = SerialHandshake.None;
            dataWriteObject = new DataWriter(serialPort.OutputStream);
        }
        else
        {
            dataWriteObject.DetachStream();
            dataWriteObject.DetachBuffer();
            dataWriteObject.Dispose();

            dataReaderObject.DetachStream();
            dataReaderObject.Dispose();

            serialPort.BaudRate = baudRate;

            dataWriteObject = new DataWriter(serialPort.OutputStream);
        }
    }

    async Task<bool> onewireReset(string deviceId)
    {
        try
        {
            await ReconfigurePort(9600, deviceId);

            dataWriteObject.WriteByte(0xF0);
            await dataWriteObject.StoreAsync();

            dataReaderObject = new DataReader(serialPort.InputStream);
            await dataReaderObject.LoadAsync(1);

            byte resp = dataReaderObject.ReadByte();
            if (resp == 0xFF)
            {
                //System.Diagnostics.Debug.WriteLine("Nothing connected to UART");
                return false;
            }
            else if (resp == 0xF0)
            {
                //System.Diagnostics.Debug.WriteLine("No 1-wire devices are present");
                return false;
            }
            else
            {
                //System.Diagnostics.Debug.WriteLine("Response: " + resp);
                await ReconfigurePort(115200, deviceId);
                dataReaderObject = new DataReader(serialPort.InputStream);
                return true;
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine("Exception: " + ex.Message);
            return false;
        }
    }

    public async Task onewireWriteByte(byte b)
    {
        for (byte i = 0; i < 8; i++, b = (byte)(b >> 1))
        {
            // Run through the bits in the byte, extracting the
            // LSB (bit 0) and sending it to the bus
            await onewireBit((byte)(b & 0x01));
        }
    }

    async Task<byte> onewireBit(byte b)
    {
        var bit = b > 0 ? 0xFF : 0x00;
        dataWriteObject.WriteByte((byte)bit);
        await dataWriteObject.StoreAsync();
        await dataReaderObject.LoadAsync(1);
        var data = dataReaderObject.ReadByte();
        return (byte)(data & 0xFF);
    }

    async Task<byte> onewireReadByte()
    {
        byte b = 0;
        for (byte i = 0; i < 8; i++)
        {
            // Build up byte bit by bit, LSB first
            b = (byte)((b >> 1) + 0x80 * await onewireBit(1));
        }
        //System.Diagnostics.Debug.WriteLine("onewireReadByte result: " + b);
        return b;
    }

    public async Task<double> getTemperature(string deviceId)
    {
        double tempCelsius = -200;

        if (await onewireReset(deviceId))
        {
            await onewireWriteByte(0xCC); //1-Wire SKIP ROM command (ignore device id)
            await onewireWriteByte(0x44); //DS18B20 convert T command 
                                          // (initiate single temperature conversion)
                                          // thermal data is stored in 2-byte temperature 
                                          // register in scratchpad memory

            // Wait for at least 750ms for data to be collated
            //await Task.Delay(250);

            // Get the data
            await onewireReset(deviceId);
            await onewireWriteByte(0xCC); //1-Wire Skip ROM command (ignore device id)
            await onewireWriteByte(0xBE); //DS18B20 read scratchpad command
                                          // DS18B20 will transmit 9 bytes to master (us)
                                          // starting with the LSB

            byte tempLSB = await onewireReadByte(); //read lsb
            byte tempMSB = await onewireReadByte(); //read msb

            // Reset bus to stop sensor sending unwanted data
            await onewireReset(deviceId);

            // Log the Celsius temperature
            tempCelsius = ((tempMSB * 256) + tempLSB) / 16.0;
            var temp2 = ((tempMSB << 8) + tempLSB) * 0.0625; //just another way of calculating it

            //System.Diagnostics.Debug.WriteLine("Temperature: " + tempCelsius + " degrees C " + temp2);
        }
        return tempCelsius;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM