Модераторы: Partizan, gambit

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Потоки, взаимодействие и работа, Практическое применение 
:(
    Опции темы
WantToProg
Дата 15.2.2010, 10:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 88
Регистрация: 13.4.2009

Репутация: нет
Всего: нет



Доброе время суток. Извиняюсь что так написал название темы, но суть оно отражает. А проблема в следующем:
Я пишу код, который в одном потоке считывает данные из файла и передает их в последовательный порт компьютера. Второй поток возникает по событию DataReceived.
Так вот проблема возникла в связи с тем, что мне необходимо закрывать порт по окончании приема данных, и открывать его снова, при отправке. Но вероятно по причине параллельности выполнения потоков, порт не успевает закрываться. Но если проверяю проверяю с точками останова, ошибки не возникает. 
Подскажите пожалуйста каким образом можно избежать данной ошибки.
Насколько я понимаю, необходимо сделать так, что бы первый поток не выполнялся пока не завершится поток по событию DataReceived, но как это сделать, я не знаю.
Проблемный код предоставляю ниже:
Код

private void ReadSelected_Click(object sender, EventArgs e)
        {
            if (HubsCollection.CheckedItems.Count != 0)
            {               
                thread = new Thread(Do);
                thread.IsBackground = false;
                thread.Start();
            }
            else
            {
                MessageBox.Show("Выберите опрашиваемые концентраторы");
            }
        }
        private void Do()
        {
            string HubFile = @"c:\counters\data\Hubs.cts";
            string con, q, DST_ADDRESS, COUNTER_ADDRESS, COM_PORT_ADDR;
            
            StreamReader StrReader = new StreamReader(HubFile, System.Text.Encoding.Default);
            List<string> HubData = new List<string>();
            while ((con = StrReader.ReadLine()) != null)
            {
                HubData.Add(con);
            }

            for (int y = 0; y < HubData.Count(); y++)
            {                
                q = HubData[y];
                string[] Words = q.Split('!');
                string hex = "0x";
                DST_ADDRESS = Words[2];
                COM_PORT_ADDR = Words[3];
                COUNTER_ADDRESS = Words[5];

                Invoke(new ReceiveHandler(UpdateDisplay), new object[] { "Опрашиваем концентратор: " + DST_ADDRESS + " Счетчик: " + COUNTER_ADDRESS + " Порт: " + COM_PORT_ADDR});

                byte[] bytes = new byte[1024];

                
                string DST_ADD_LF = hex + DST_ADDRESS.Substring(0, 2);
                string DST_ADD_RT = hex + DST_ADDRESS.Substring(2);

                string COUNTER_ADDRESS_LF = hex + COUNTER_ADDRESS.Substring(0, 2);
                string COUNTER_ADDRESS_RT = hex + COUNTER_ADDRESS.Substring(2);


                byte D_A_L = (Convert.ToByte(DST_ADD_LF, 16));
                byte D_A_R = (Convert.ToByte(DST_ADD_RT, 16));
                byte C_A_L = (Convert.ToByte(COUNTER_ADDRESS_LF, 16));
                byte C_A_R = (Convert.ToByte(COUNTER_ADDRESS_RT, 16));

                byte[] data_conf = { 0xff, 0xff, D_A_R, D_A_L, 0x01 };                
                long CRC = CRC24(data_conf);                
                byte[] CRC_24_S = BitConverter.GetBytes(CRC);
                
                //Чтение версии прошивки концентратора
                byte[] GET_VERINFO = { CRC_24_S[0], CRC_24_S[1], CRC_24_S[2], 0xff, 0xff, D_A_R, D_A_L, 0x01, 0x83, 0x82 };
                //чтение текущего времени на коцентраторе
                byte[] GET_TIMEDATE = { CRC_24_S[0], CRC_24_S[1], CRC_24_S[2], 0xff, 0xff, D_A_R, D_A_L, 0x01, 0x81, 0x80 };
                //чтение текущей конфигурации
                byte[] GET_CONFIG = { CRC_24_S[0], CRC_24_S[1], CRC_24_S[2], 0xff, 0xff, D_A_R, D_A_L, 0x01, 0x80, 0x7f };                
                //чтение информации с почтовых ящиков
                byte[] data_rc = { 0xff, 0xff, D_A_R, D_A_L, 0x03 };
                long CRC_1 = CRC24(data_rc);
                byte[] CRC_24_RC = BitConverter.GetBytes(CRC);
                byte[] GET_MAIL = { CRC_24_RC[0], CRC_24_RC[1], CRC_24_RC[2], 0xff, 0xff, D_A_R, D_A_L, 0x03, 0x82, C_A_R, C_A_L, 0x84 };

                System.IO.Ports.SerialPort newPort = new SerialPort(COM_PORT_ADDR, 38400, Parity.None, 8, StopBits.One);
                newPort.ReadTimeout = 1000;
                                
                newPort.RtsEnable = false;
                newPort.DtrEnable = true;

                if (newPort.IsOpen == true)
                {
                    newPort.Close();
                    System.Threading.Thread.Sleep(5 * 1000);
                }
                //Открываем порт и отправляем данные
                newPort.Open();
                newPort.DataReceived += new SerialDataReceivedEventHandler(newPort_DataReceived);
                if (newPort.CtsHolding == true) { } else { };
                Send(newPort, GET_VERINFO, GET_TIMEDATE, GET_CONFIG, GET_MAIL);                
            }
        }

        private void Send(SerialPort newPort, byte[] GET_VERINFO, byte[] GET_TIMEDATE, byte[] GET_CONFIG, byte[] GET_MAIL)
        {
            newPort.Write(GET_VERINFO, 0, GET_VERINFO.Length);
            newPort.Write(GET_TIMEDATE, 0, GET_TIMEDATE.Length);
            newPort.Write(GET_CONFIG, 0, GET_CONFIG.Length);
            newPort.Write(GET_MAIL, 0, GET_MAIL.Length);
        } 

        void newPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort port = sender as SerialPort;
            Thread.Sleep(1000);
            int byteRecieved = port.BytesToRead;            
            byte[] messByte = new byte[byteRecieved];
            port.Read(messByte, 0, byteRecieved);            
            port.Close();
            string RecvMsgs = Convert.ToString(messByte.Count());
            Invoke(new ReceiveHandler(UpdateDisplay), new object[] {RecvMsgs});            
        } 

PS: Причина, по которой порт необходимо закрывать по окончании приема данных, заключается в том, что в файле, который я читаю в начале порты могут быть разные (образец строки из файла 1!2380!23d8!COM3!1!0003!), а считывать и получать данные от устройств мне необходимо последовательно.
PPS: Я не программист, так что сильно не ругайте за код, просто приходится параллельно выполнять еще и такие задачи. Прислушаюсь к хорошим советам по улучшению кода.

Это сообщение отредактировал(а) WantToProg - 15.2.2010, 11:58
PM MAIL   Вверх
Экскалупатор
Дата 15.2.2010, 13:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1746
Регистрация: 1.4.2009
Где: г. Минск

Репутация: 11
Всего: 24



во-первых, надо закрывать порт не в if, а просто в конце кода. т.е. закрывать в любом случае.
Код

//Открываем порт и отправляем данные
                newPort.Open();
                newPort.DataReceived += new SerialDataReceivedEventHandler(newPort_DataReceived);
                if (newPort.CtsHolding == true) { } else { };
                Send(newPort, GET_VERINFO, GET_TIMEDATE, GET_CONFIG, GET_MAIL);    
newPort.Close();

во-вторых, попробуй использовать блок
Код

using ()
{
}

оберни в него блок кода который работает с портом. должно получиться как то так:
Код

using(System.IO.Ports.SerialPort newPort = new SerialPort(COM_PORT_ADDR, 38400, Parity.None, 8, StopBits.One))
{
                newPort.ReadTimeout = 1000;                                
                newPort.RtsEnable = false;
                newPort.DtrEnable = true;
                if (newPort.IsOpen == true)
                {
                    newPort.Close();
                    System.Threading.Thread.Sleep(5 * 1000);
                }
                //Открываем порт и отправляем данные
                newPort.Open();
                newPort.DataReceived += new SerialDataReceivedEventHandler(newPort_DataReceived);
                if (newPort.CtsHolding == true) { } else { };
                Send(newPort, GET_VERINFO, GET_TIMEDATE, GET_CONFIG, GET_MAIL); 
}

в таком случае все манипуляции с закрытием и ожиданием должны отпасть.

по поводу организации кода говорить надо много и долго. по сути тут надо переделывать все. возьми книжку:
Стив Макконел "Совершенный код".
Мартин Фаулер "Рефакторинг".
PM MAIL ICQ   Вверх
WantToProg
Дата 15.2.2010, 14:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 88
Регистрация: 13.4.2009

Репутация: нет
Всего: нет



Цитата(Экскалупатор @  15.2.2010,  13:22 Найти цитируемый пост)
во-первых, надо закрывать порт не в if, а просто в конце кода. т.е. закрывать в любом случае.

Но в таком случае порт на момент получения данных будет уже закрыт.

И с блоком using не все так хорошо. Порт открывается, и тут же закрывается, не получив данные. Возможно я не очень хорошо разбираюсь с даным блоком, но как его использовать в моем коде, я к сожалению не понимаю.
Может быть есть аналог WinAPI в C#: EnterCriticalSection, LeaveCriticalSection, WaitForSingleObject?
PM MAIL   Вверх
Экскалупатор
Дата 15.2.2010, 14:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1746
Регистрация: 1.4.2009
Где: г. Минск

Репутация: 11
Всего: 24



Цитата(WantToProg @  15.2.2010,  13:18 Найти цитируемый пост)
Но в таком случае порт на момент получения данных будет уже закрыт.

значит в таком случае у тебя ошибка в организации всего процесса. как то странно все работает. такое ощущение что сначала слушается порт, а потом открывается новый и при следующем цикле используется старый открытый порт.
PM MAIL ICQ   Вверх
WantToProg
Дата 15.2.2010, 15:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 88
Регистрация: 13.4.2009

Репутация: нет
Всего: нет



Цитата(Экскалупатор @  15.2.2010,  14:53 Найти цитируемый пост)
значит в таком случае у тебя ошибка в организации всего процесса. как то странно все работает. такое ощущение что сначала слушается порт, а потом открывается новый и при следующем цикле используется старый открытый порт. 

тут наверно я что то не понимаю. Весь код предоставлен выше, ничего другого нет.

Если я выполняю дебагинг то получаю следующее:
1. Запускается цикл, в котором получаем необходимые значения для отправки в порт
2. Создается экземпляр порта с необходимыми параметрами
3. Порт открывается
4. Выполняется функция отправки данных
5. Возвращается на место вызова функции
6. Порт закрывается //Вы советовали
7. Параллельно запускается прием данных с порта по событию
8. Данные не могут быть получены и мы получаем Exception IsClosed

Прошу, объясните поподробнее, что должно было произойти по вашему мнению. Возможно я что то не так сделал.
PM MAIL   Вверх
Экскалупатор
Дата 15.2.2010, 15:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1746
Регистрация: 1.4.2009
Где: г. Минск

Репутация: 11
Всего: 24



Цитата(WantToProg @  15.2.2010,  14:12 Найти цитируемый пост)
6. Порт закрывается //Вы советовали
7. Параллельно запускается прием данных с порта по событию
8. Данные не могут быть получены и мы получаем Exception IsClosed

я не учитывал что нужно получать данные.

в таком случае нужно ждать пока закончиться прием данных, а потом все закрывать и начинать сначала.
PM MAIL ICQ   Вверх
uranpro
Дата 15.2.2010, 16:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 571
Регистрация: 7.5.2008
Где: Moscow city

Репутация: 1
Всего: 1



Цитата

На практике рекомендуется после вызова метода Close подождать некоторое время, прежде чем вызывать метод Open, поскольку порт не всегда закрывается мгновенно.

из справки =)


--------------------
I want a perfect soul
PM MAIL ICQ   Вверх
WantToProg
Дата 15.2.2010, 16:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 88
Регистрация: 13.4.2009

Репутация: нет
Всего: нет



Цитата(uranpro @  15.2.2010,  16:20 Найти цитируемый пост)
На практике рекомендуется после вызова метода Close подождать некоторое время, прежде чем вызывать метод Open, поскольку порт не всегда закрывается мгновенно.

Не помогает, т.к. процедура чтения данных по событию, выполняется параллельно потоку Do(). И открытие порта инициируется в этом потоке до того, как порт будет закрыт и данные будут прочитаны
Чувтсвую в коде есть ошибка, но найти не знаю как.
Мне все таки кажется есть возможность не выполнять цикл процедуры Do(), до тех пор, пока не будут получены данные. А вот как это сделать, очень прошу у вас совета

PM MAIL   Вверх
Экскалупатор
Дата 15.2.2010, 16:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1746
Регистрация: 1.4.2009
Где: г. Минск

Репутация: 11
Всего: 24



uranpro, да. согласен, но тут вроде немного не так все. тут смысл в том что в одном потоке открывается порт, а второй поток этот открытый порт использует(ну на сколько я понял из объяснений). получается что второй поток зависит от первого но он об этом не знает. отсюда и лезет ошибка.
Цитата(WantToProg @  15.2.2010,  15:31 Найти цитируемый пост)
процедура чтения данных по событию, выполняется параллельно потоку Do(). 

но ведь это не правильно? на сколько я понимаю сначала идет отправка чего то в порт, а потом читается ответ. ну так надо так и сделать этот процесс:
открыть порт
отправить
прочитать ответ
закрыть порт

или ставить событие которое будет ждать команды окончания чтения из порта, и будет закрывать порт и только потом начинать новую итерацию цикла.
PM MAIL ICQ   Вверх
WantToProg
Дата 15.2.2010, 18:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 88
Регистрация: 13.4.2009

Репутация: нет
Всего: нет



Цитата(Экскалупатор @  15.2.2010,  16:46 Найти цитируемый пост)
но ведь это не правильно? на сколько я понимаю сначала идет отправка чего то в порт, а потом читается ответ. ну так надо так и сделать этот процесс:
открыть порт
отправить
прочитать ответ
закрыть порт

или ставить событие которое будет ждать команды окончания чтения из порта, и будет закрывать порт и только потом начинать новую итерацию цикла. 

Вот, вы меня отлично поняли.
Открыть порт, отправить - первый поток из процедуры Do()
Прочитать ответ по событию newPort.DataReceived += new SerialDataReceivedEventHandler(newPort_DataReceived);
Вот тут описание работы события http://msdn.microsoft.com/en-us/library/sy...tareceived.aspx

Как мне сделать, чтоб не возникло проблемы параллельного выполнения потока. Может быть вообще поток лишнее? Но тогда в какую сторону копать?
PM MAIL   Вверх
Экскалупатор
Дата 15.2.2010, 20:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1746
Регистрация: 1.4.2009
Где: г. Минск

Репутация: 11
Всего: 24



WantToProg, я, конечно, не возьмусь утверждать, но мне кажется что поскольку ответ всегда читается после отправки, то нет никакого смысла делить этот процесс на два параллельных потока. все должно быть по порядку: открыть, отправить, прочитать, закрыть, следующая итерация. я это и пытался сказать, что деление на потоки в данном случае мне кажется бессмысленным, поскольку эти операции идут одна за другой. единственное нужно будет учесть превышение времени ожидания ответа.
PM MAIL ICQ   Вверх
WantToProg
Дата 16.2.2010, 08:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 88
Регистрация: 13.4.2009

Репутация: нет
Всего: нет



Экскалупатор, В том то и проблема, я не знаю как сделать все это в одном потоке :(
Обработку события newPort.DataReceived, я взял с msdn. А другого варианта я даже не вижу. Если бы это был сетевой поток (NetworkStream), то я обошелся бы событием DataAvailable, но как проверить наличие данных в последовательном порту, я не знаю.

И еще, дело в том, что есть другой вариант считывания данных: SerialPort.ReadExisting()
Который возвращает System.String, но мне надо получить поток байтов. Для этого есть заметка в этой статье
Цитата

Если необходимо переключиться между чтением текста и чтением двоичных данных из потока, выберите протокол, четко различающий текст и двоичные данные, например чтение байтов и декодирование данных вручную.

Не понимаю как это сделать.

Это сообщение отредактировал(а) WantToProg - 16.2.2010, 09:56
PM MAIL   Вверх
Экскалупатор
Дата 16.2.2010, 12:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1746
Регистрация: 1.4.2009
Где: г. Минск

Репутация: 11
Всего: 24



WantToProg, можно попробовать "усыпить" поток на время выполнения получения данных. ты видно пытался это сделать:
Код

Thread.Sleep(1000);


но так усыпляется тот поток в котором ты вызываешь этот метод. но при этом поток в котором открывается порт продолжает работать и закрывает порт.
из MSDN
Цитата

Событие DataReceived необязательно наступает для каждого полученного байта. Для определения количества данных в буфере, которые осталось прочитать, используйте свойство BytesToRead.

может это можно как то использовать? получается запрашиваешь число байтов, потом их считываешь, а потом продолжаешь выполнять метод.
PM MAIL ICQ   Вверх
uranpro
Дата 16.2.2010, 12:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 571
Регистрация: 7.5.2008
Где: Moscow city

Репутация: 1
Всего: 1



а если сделать метод не Do, а DoNext(...) и вызывать в конце DataReceived?


--------------------
I want a perfect soul
PM MAIL ICQ   Вверх
WantToProg
Дата 16.2.2010, 12:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 88
Регистрация: 13.4.2009

Репутация: нет
Всего: нет



Цитата(Экскалупатор @  16.2.2010,  12:16 Найти цитируемый пост)
но так усыпляется тот поток в котором ты вызываешь этот метод. но при этом поток в котором открывается порт продолжает работать и закрывает порт.

Во, я так и сделал. В принципе работает, хотя помоему это неправильно. Думаю правильнее было бы блокировать первый поток пока не закончится второй.
А проще было бы читать сразу по типу SerialPort.ReadExisting(). Только вот последовательность байтов не преобразовывая в строку.
Если бы вы еще и это подсказали, был бы просто счастлив smile
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.0569 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.