asyn c await в UWP windows 10 Ядро IOT, работающее в Raspberry Pi 3B, и утечка памяти - PullRequest
1 голос
/ 04 августа 2020

Прежде всего, я полный новичок в C# и буквально не знаю, как использовать async / await / cancellation / Tasks, я пришел из встроенных систем с базовыми c -модерными знаниями C / C ++ . Итак, я просто понимаю, что здесь делают эти функции asyn c. коды задач asyn c скопированы из образцов от Microsoft, они просто продолжают считывать последовательный порт для получения новых байтов. Я сделал несколько анимаций для пользовательского интерфейса через Blend, и в этом нет ничего более изысканного.

Это приложение работает на малине с ядром IOT, и его единственная задача - предоставить пользовательский интерфейс для управления микроконтроллером с помощью текстовые инструкции отправляются через последовательный порт. Все эти кнопки просто отправляют некоторые строки на последовательный порт, который мой микроконтроллер считывает и действует соответственно.

Проблема 1: Я изменил строку в ReadAsyn c функцию uint ReadBufferLength = 1; значение было 128 в образце кода, мой микроконтроллер отправляет полную инструкцию или данные, заканчивающиеся на «\ r \ n». Это означало, что он будет продолжать читать байты последовательно, которые поступают от микроконтроллера, до тех пор, пока буфер не заполнится, и снова вызовет функцию ReadAsyn c. поскольку он читал до тех пор, пока буфер (128) не был заполнен, приходит несколько инструкций и заполняет буфер. Так что у меня не было возможности выяснить, какая инструкция в буфере. Итак, моя работа заключалась в том, чтобы изменить буфер на 1 и добавить строку с новыми байтами из последовательного порта, пока он не найдет Environment.NewLine, а затем построить logi c на основе полной инструкции в строке.

Вопрос 1: Как мне изменить код, чтобы не изменять uint ReadBufferLength = 1; и заставить функцию ReadAsyn c хранить мои последовательные входящие данные, пока не найдет \ r \ n?

MAJOR Проблема 2: как только я вхожу в ручной / автоматический режим, щелкая кнопку на передней панели, мой микроконтроллер начинает отправлять значение датчика температуры через последовательный порт как можно быстрее без каких-либо задержек между инструкциями. Я знаю, что raspberry pi (ядро win10 iot) достаточно быстр, чтобы не пропустить ни одной инструкции или переполнения физического буфера последовательного порта PI. Работает нормально, на данный момент в приложении нет проблем, нет cra sh или замедлений. Но со временем я вижу, что приложение потребляет все больше и больше оперативной памяти. поведение такое же в P C, если я построю его в режиме x86. после работы в течение 1 часа он будет потреблять более 120 мегабайт, тогда как изначально он начинается с 30-50 мегабайт.

Чтобы воссоздать проблему, измените string qFilter = SerialDevice.GetDeviceSelector("UART0"); UART0 на соответствующий com-порт в windows. иначе приложение не запустится. Вероятно, необходимо установить виртуальный COM-порт или может потребоваться модуль usb to ttl.

Проблема 3: Невозможно собрать приложение в режиме выпуска с помощью параметра «Скомпилировать с. NET Native tool chain», без этого он отлично работает в малине.

Internal compiler error: MCG0024:UnresolvableTypeReference Unresolvable type reference 'System.Runtime.InteropServices.WindowsRuntime.IRestrictedErrorInfo' in 'Assembly(Name=System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)' found. Please check the references in your build system. A reference is either missing or an assembly is missing an expected type. hvac_version_4 файл проекта github

using Windows.UI;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.Devices.SerialCommunication;
using Windows.Storage.Streams;
using System.Threading;
using Windows.Devices.Enumeration;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Animation;
using Microsoft.Toolkit.Uwp.UI.Animations.Behaviors;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace hvac_version_2
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        SolidColorBrush greenBrush = new SolidColorBrush(Colors.Green);
        SolidColorBrush redBrush = new SolidColorBrush(Colors.Red);
        SolidColorBrush greyBrush = new SolidColorBrush(Colors.DarkSlateGray);

        //private bool updatingValues;


        private CancellationTokenSource ReadCancellationTokenSource;
        private SerialDevice serialPort = null;
        DataWriter dataWriteObject = null;
        DataReader dataReaderObject = null;
        public string strFromPort;
        private string final_string;
        
        private int compressorAkey=0;
        private int compressorBkey=0;
        private int pump_entry = 0;
        private int compAentry=0;
        private int compBentry=0;
        private int fan1entry = 0;
        private int fan2entry = 0;
        private double Blur_amount = 0;

        public MainPage()
        {
            this.InitializeComponent();
            stop_button.IsEnabled = false;
            set_temperature.IsEnabled = false;
            set_switching_time.IsEnabled = false;
            auto_mode.IsEnabled = false;
            manual_mode.IsEnabled = false;
            compressorA_button.IsEnabled = false;
            compressorB_button.IsEnabled = false;
            fanA_button.IsEnabled = false;
            fanB_button.IsEnabled = false;
            init.IsEnabled = false;
            init_timing.IsEnabled = false;
            pump_button.IsEnabled = false;
            load_animation.Begin();
            
        }

        private async void stop_button_Click(object sender, RoutedEventArgs e)
        {
            //do something
            start_animation.Stop();
            if (serialPort == null) return;
            await sendToPort("end");
            set_temperature.IsEnabled = false;
            set_switching_time.IsEnabled = false;
            manual_mode.IsEnabled = true;
            auto_mode.IsEnabled = true;
            stop_button.IsEnabled = false;
            compressorA_button.IsEnabled = false;
            compressorB_button.IsEnabled = false;
            fanA_button.IsEnabled = false;
            fanB_button.IsEnabled = false;
            init.IsEnabled = false;
            init_timing.IsEnabled = false;
            pump_button.IsEnabled = false;
            start_button.IsEnabled = true;
            pump_entry = 0;
            compAentry = 0;
            compBentry = 0;
            fan1entry = 0;
            fan2entry = 0;
            stop_animation.Begin();
            start_button.Width = 162;
            GC.Collect();
        }

        
        
        private async void set_temperature_changed(object sender, EventArgs e)
        {
            //System.Diagnostics.Debug.WriteLine("entered the async void set_temp.............");
            
            
        }

        private async void set_switching_time_value_changed(object sender, EventArgs e)
        {
            //System.Diagnostics.Debug.WriteLine("entered the async void switching time.............");
            
            

        }

        private async void manual_mode_Click(object sender, RoutedEventArgs e)
        {
            if (serialPort == null) return;
            await sendToPort("manual");
            auto_mode.IsEnabled = false;
            manual_mode.IsEnabled = false;
            stop_button.IsEnabled = true;
            set_temperature.IsEnabled = true;
            set_switching_time.IsEnabled = true;
            pump_button.IsEnabled = true;
            fanA_button.IsEnabled = true;
            fanB_button.IsEnabled = true;
            compressorA_button.IsEnabled = true;
            compressorB_button.IsEnabled = true;
            init.IsEnabled = true;
            init_timing.IsEnabled = true;
            
        }

        private async void auto_mode_Click(object sender, RoutedEventArgs e)
        {
            set_switching_time.IsEnabled = true;
            set_temperature.IsEnabled = true;
            init.IsEnabled = true;
            stop_button.IsEnabled = true;
            init_timing.IsEnabled = true;
            if (serialPort == null) return;
            await sendToPort("auto");

        }

        private async void start_button_Click(object sender, RoutedEventArgs e)
        {
            stop_animation.Stop();
            await sendToPort("ready");
            auto_mode.IsEnabled = true;
            manual_mode.IsEnabled = true;
            start_button.IsEnabled = false;
            start_animation.Begin();
            System.Diagnostics.Debug.WriteLine("sending ready command");
            
            
        }

        private async void compressorA_button_Click(object sender, RoutedEventArgs e)
        {
            if (compAentry == 0)
            {
                if (serialPort == null) return;
                await sendToPort("compressorAON");   //sending key
                compAentry = 1;
            }
            else
            {
                if (serialPort == null) return;
                await sendToPort("compressorAoff");  //sending key
                compAentry = 0;
            }
        }

        private async void compressorB_button_Click(object sender, RoutedEventArgs e)
        {
            if (compBentry == 0)
            {
                if (serialPort == null) return;
                await sendToPort("compressorBON");   //sending key
                compBentry = 1;
            }
            else
            {
                if (serialPort == null) return;
                await sendToPort("compressorBoff");  //sending key
                compBentry = 0;
            }
        }

        private async void fanA_button_Click(object sender, RoutedEventArgs e)
        {
            if (fan1entry == 0)
            {
                if (serialPort == null) return;
                await sendToPort("fan1on"); 
                fan1entry = 1;
            }
            else
            {
                if (serialPort == null) return;
                await sendToPort("fan1off");  
                fan1entry = 0;
            }
        }

        private async void fanB_button_Click(object sender, RoutedEventArgs e)
        {
            if (fan2entry == 0)
            {
                if (serialPort == null) return;
                await sendToPort("fan2on");
                fan2entry = 1;
            }
            else
            {
                if (serialPort == null) return;
                await sendToPort("fan2off");
                fan2entry = 0;
            }
        }

        private async void pump_button_Click(object sender, RoutedEventArgs e)
        {
            if (pump_entry == 0)
            {
                if (serialPort == null) return;
                await sendToPort("pumpon");
                pump_entry = 1;
            }
            else
            {
                if (serialPort == null) return;
                await sendToPort("pumpoff");
                pump_entry = 0;
            }

        }

        private async void Page_Loaded(object sender, RoutedEventArgs e)
        {
            string qFilter = SerialDevice.GetDeviceSelector("UART0");
            DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(qFilter);

            if (devices.Any())
            {
                string deviceId = devices.First().Id;

                 await OpenPort(deviceId);
            }

            ReadCancellationTokenSource = new CancellationTokenSource();

            while (true)
            {
                
                //System.Diagnostics.Debug.WriteLine("program came before await listen");
                await Listen();
            }
        }


        
        private async Task OpenPort(string deviceId)
        {
            serialPort = await SerialDevice.FromIdAsync(deviceId);

            if (serialPort != null)
            {
                serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
                serialPort.ReadTimeout = TimeSpan.FromMilliseconds(50);
                serialPort.BaudRate = 115200;
                serialPort.Parity = SerialParity.None;
                serialPort.StopBits = SerialStopBitCount.One;
                serialPort.DataBits = 8;
                serialPort.Handshake = SerialHandshake.None;
                //Console.WriteLine("Serial port configured successfully");
                //txtStatus.Text = "Serial port configured successfully";
                

            }
        }

        
        private async Task Listen()
        {
            try
            {
                if (serialPort != null)
                {
                    dataReaderObject = new DataReader(serialPort.InputStream);
                    await ReadAsync(ReadCancellationTokenSource.Token);
                    
                }
            }
            catch (Exception ex)
            {
                txtStatus.Text = ex.Message;
            }
            finally
            {
                if (dataReaderObject != null)    // Cleanup once complete
                {
                    dataReaderObject.DetachStream();
                    dataReaderObject = null;
                    
                }
            }
        }
        
        private async Task ReadAsync(CancellationToken cancellationToken)
        {
            Task<UInt32> loadAsyncTask;

            uint ReadBufferLength = 1;  // only when this buffer would be full next code would be executed

            dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;

            loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken);   // Create a task object
            //string debug = dataReaderObject.ReadString(bytesRead2);
            //System.Diagnostics.Debug.WriteLine("writing string debug"+ debug);

            UInt32 bytesRead = await loadAsyncTask;    // Launch the task and wait until buffer would be full

            if (bytesRead > 0)
            {
                strFromPort = dataReaderObject.ReadString(bytesRead);
                final_string = final_string + strFromPort;
                //txtStatus2.Text = final_string;
                if (final_string.Contains(Environment.NewLine))
                {
                    string logicstring = final_string;
                    final_string = "";

                    //System.Diagnostics.Debug.WriteLine("writing logic string " + logicstring);
                    
                    if (logicstring.StartsWith("A"))
                    {   
                        if(logicstring.Contains("-"))
                        {
                            //it is a negative number
                            logicstring = Regex.Match(logicstring, @"\d+").Value;
                            int gauge1_pass = int.Parse(logicstring) * -1;
                            tempgauge1.Value = gauge1_pass;
                        }
                        else
                        {
                            logicstring = Regex.Match(logicstring, @"\d+").Value;
                            int gauge1_pass = int.Parse(logicstring);
                            tempgauge1.Value = gauge1_pass;
                        }
                    }



                    if (logicstring.StartsWith("B"))
                    {
                        if (logicstring.Contains("-"))
                        {
                            //it is a negative number
                            logicstring = Regex.Match(logicstring, @"\d+").Value;
                            int gauge2_pass = int.Parse(logicstring) * -1;
                            tempgauge1.Value = gauge2_pass;
                        }
                        else
                        {
                            logicstring = Regex.Match(logicstring, @"\d+").Value;
                            int gauge2_pass = int.Parse(logicstring);
                            tempgauge2.Value = gauge2_pass;
                        }
                    }
                    if (logicstring.StartsWith("setting"))
                    {
                        if (logicstring.Contains("-"))
                        {
                            //it is a negative number
                            logicstring = Regex.Match(logicstring, @"\d+").Value;
                            int previous_temp_setting = int.Parse(logicstring) * -1;
                            set_temperature.Value = previous_temp_setting;
                        }
                        else
                        {
                            logicstring = Regex.Match(logicstring, @"\d+").Value;
                            int previous_temp_setting = int.Parse(logicstring);
                            set_temperature.Value = previous_temp_setting;
                        }
                    }

                    if (logicstring.StartsWith("pumpon"))
                    {
                        pump_status_light.Fill = greenBrush;
                        Storyboard_Pump_Led.RepeatBehavior = RepeatBehavior.Forever;
                        Storyboard_Pump_Led.Begin();

                    }
                    if (logicstring.StartsWith("pumpoff"))
                    {   
                        pump_status_light.Fill = redBrush;
                        Storyboard_Pump_Led.Stop();
                    }
                    if (logicstring.StartsWith("pumpfail"))
                    {
                        if (!FailPopup.IsOpen) 
                        { 
                            FailPopup.IsOpen = true; 
                        }
                    }
                    if (logicstring.StartsWith("clear the popup"))
                    {
                        if (FailPopup.IsOpen) 
                        {
                            FailPopup.IsOpen = false;
                        }
                    }
                    if (logicstring.StartsWith("switch_interval"))
                    {
                        
                            
                            logicstring = Regex.Match(logicstring, @"\d+").Value;
                            int previous_interval_setting = int.Parse(logicstring);
                            set_switching_time.Value = previous_interval_setting;


                    }
                    if (logicstring.StartsWith("timer"))
                    {


                        logicstring = Regex.Match(logicstring, @"\d+").Value;
                        int remaining_time = int.Parse(logicstring);
                        remaining_time = remaining_time / 1000;
                        timer_box.Text = remaining_time.ToString();


                    }
                    if (logicstring.StartsWith("Free RAM = "))
                    {


                        logicstring = Regex.Match(logicstring, @"\d+").Value;
                        int remaining_memory = int.Parse(logicstring);
                        free_memory.Text = remaining_memory.ToString();


                    }
                    if (logicstring.StartsWith("fan1on"))
                    {
                        fan1_status_light.Fill = greenBrush;
                        storyboard_fan1_led.RepeatBehavior = RepeatBehavior.Forever;
                        storyboard_fan1_led.Begin();
                        
                    }
                    if (logicstring.StartsWith("fan1off"))
                    {
                        fan1_status_light.Fill = redBrush;
                        storyboard_fan1_led.Stop();
                    }
                    if (logicstring.StartsWith("fan2on"))
                    {
                        fan2_status_light.Fill = greenBrush;
                        storyboard_fan_2.RepeatBehavior = RepeatBehavior.Forever;
                        storyboard_fan_2.Begin();
                    }
                    if (logicstring.StartsWith("fan2off"))
                    {
                        fan2_status_light.Fill = redBrush;
                        storyboard_fan_2.Stop();
                    }
                    if (logicstring.StartsWith("compressorAON"))   //key
                    {
                        compressorA_status_light.Fill = greyBrush;
                        compressorAkey = 1;
                    }
                    if (logicstring.StartsWith("compressorAoff"))  //key
                    {
                        compressorA_status_light.Fill = redBrush;
                        compressorAkey = 0;
                    }
                    if (logicstring.StartsWith("compressorBON"))  //key
                    {
                        compressorB_status_light.Fill = greyBrush;
                        compressorBkey = 1;
                    }
                    if (logicstring.StartsWith("compressorBoff")) //key
                    {
                        compressorB_status_light.Fill = redBrush;
                        compressorBkey = 0;
                    }
                    if (logicstring.StartsWith("compressorA on")) // actually compressor on
                    {
                        compressorA_status_light.Fill = greenBrush;
                        storyboard_compA_Led.RepeatBehavior = RepeatBehavior.Forever;
                        storyboard_compA_Led.Begin();
                    }
                    if (logicstring.StartsWith("compressorB on")) // actually compressor on
                    {
                        compressorB_status_light.Fill = greenBrush;
                        storyboard_compB_led.RepeatBehavior = RepeatBehavior.Forever;
                        storyboard_compB_led.Begin();
                    }

                    if (logicstring.StartsWith("compressor off")) // actually compressor off
                    {
                        if (compressorBkey == 1)
                        {
                            compressorB_status_light.Fill = greyBrush;
                            storyboard_compB_led.Stop();
                        }
                        if (compressorAkey == 1)
                        {
                            compressorA_status_light.Fill = greyBrush;
                            storyboard_compA_Led.Stop();
                        }
                        if (compressorBkey == 0)
                        {
                            compressorB_status_light.Fill = redBrush;
                            storyboard_compB_led.Stop();
                        }
                        if (compressorAkey == 0)
                        {
                            compressorA_status_light.Fill = redBrush;
                            storyboard_compA_Led.Stop();
                        }
                    }
                    if (logicstring.StartsWith("compressorA off")) // actually compressor off
                    {
                        compressorA_status_light.Fill = redBrush;
                        storyboard_compA_Led.Stop();
                    }
                    if (logicstring.StartsWith("compressorB off")) // actually compressor off
                    {
                        compressorB_status_light.Fill = redBrush;
                        storyboard_compB_led.Stop();
                    }
                }
            }
        }

        private async Task WriteAsync(string text2write)
        {
            Task<UInt32> storeAsyncTask;

            if (text2write.Length != 0)
            {
                dataWriteObject.WriteString(text2write);

                storeAsyncTask = dataWriteObject.StoreAsync().AsTask();  // Create a task object

                UInt32 bytesWritten = await storeAsyncTask;   // Launch the task and wait
                if (bytesWritten > 0)
                {
                    //txtStatus.Text = bytesWritten + " bytes written at " + DateTime.Now.ToString(System.Globalization.CultureInfo.CurrentUICulture.DateTimeFormat.LongTimePattern);
                }
            }
            else { }
        }


        private async Task sendToPort(string sometext)
        {
            try
            {
                if (serialPort != null)
                {
                    dataWriteObject = new DataWriter(serialPort.OutputStream);

                    await WriteAsync(sometext);
                }
                else { }
            }
            catch (Exception ex)
            {
                txtStatus.Text = ex.Message;
            }
            finally
            {
                if (dataWriteObject != null)   // Cleanup once complete
                {
                    dataWriteObject.DetachStream();
                    dataWriteObject = null;
                }
            }
        }


        private void CancelReadTask()
        {
            if (ReadCancellationTokenSource != null)
            {
                if (!ReadCancellationTokenSource.IsCancellationRequested)
                {
                    ReadCancellationTokenSource.Cancel();
                }
            }
        }
        
        private void Page_Unloaded(object sender, RoutedEventArgs e)
        {
            CancelReadTask();
            if (serialPort != null)
            {
                serialPort.Dispose();
            }
            serialPort = null;
        }

        private async void init_Click(object sender, RoutedEventArgs e)
        {
            double sendtempvalue = (double)set_temperature.Value;
            if (serialPort == null) return;
            await sendToPort("settemp," + sendtempvalue);
            //System.Diagnostics.Debug.WriteLine("EEPROM " + sendtempvalue);

            

        }

        private async void init_timing_Click(object sender, RoutedEventArgs e)
        {
            double switching_time_minute = (double)set_switching_time.Value;
            if (serialPort == null) return;
            await sendToPort("switch_interval," + switching_time_minute);
            //System.Diagnostics.Debug.WriteLine("EEPROM " + switching_time_minute);
        }

        

        /*
        private void Page_Unloaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            
        }*/
    }
}

1 Ответ

1 голос
/ 04 августа 2020

Вопрос 1. Как мне изменить код, чтобы не изменять uint ReadBufferLength = 1; и сделать так, чтобы функция ReadAsyn c сохраняла мои последовательные входящие данные до тех пор, пока не найдет \ r \ n?

Попробуйте использовать следующий код. При использовании ReadBufferLength = 1 эффективность передачи будет ниже.

    private async Task ReadAsync(CancellationToken cancellationToken)
    {
        Task<UInt32> loadAsyncTask;

        uint ReadBufferLength = 128;  // only when this buffer would be full next code would be executed

        dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;

        loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken);   // Create a task object
        //string debug = dataReaderObject.ReadString(bytesRead2);
        //System.Diagnostics.Debug.WriteLine("writing string debug"+ debug);

        UInt32 bytesRead = await loadAsyncTask;    // Launch the task and wait until buffer would be full

        if (bytesRead > 0)
        {
            strFromPort = dataReaderObject.ReadString(bytesRead);
            final_string = final_string + strFromPort;
            //txtStatus2.Text = final_string;
            if (final_string.Contains(Environment.NewLine))
            {
                string logicstring = final_string;
                final_string = final_string.Substring(final_string.IndexOf(Environment.NewLine) + 1);

                //System.Diagnostics.Debug.WriteLine("writing logic string " + logicstring);
                
            }
        }
    }

Проблема 2: после запуска в течение 1 часа он будет потреблять более 120 мегабайт, тогда как изначально он начинается с 30-50 мегабайт.

Пожалуйста, попробуйте собрать приложение в выпуске, но не используя собственный набор инструментов. NET. Это известная проблема. См. здесь .

Проблема 3. Невозможно собрать приложение в режиме выпуска с помощью параметра «Скомпилировать с. NET собственной цепочкой инструментов», без этого оно отлично работает в малина.

Удалите ссылки runtime.win10-arm-aot.Microsoft.NETCore.UniversalWindowsPlatform и runtime.win10-arm.Microsoft.NETCore.UniversalWindowsPlatform в своем проекте. Проект будет успешно скомпилирован с собственной цепочкой инструментов. Net без двух библиотек, поскольку эти две библиотеки зависят от собственной библиотеки System.Runtime.InteropServices.

...