Форум программистов, компьютерный форум, киберфорум
C# Windows Forms
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.84/89: Рейтинг темы: голосов - 89, средняя оценка - 4.84
3 / 3 / 4
Регистрация: 13.05.2016
Сообщений: 38

Обработка данных с COM порта без использования Sleep

30.06.2016, 17:04. Показов 17990. Ответов 17
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
делаю проект, который взаимодействует с реальным устройством через COM-PORT
я отправляю команду, жду (я сделал через sleep) пока в serialPort1_DataReceived соберётся стройка (ибо получаю по 1 символу от порта), в нём же через делегат обрабатываю строчку(которую собираю) и если строчка правильная (обрезаю её, ибо получаю ответы с эхом (то есть команда+ответ)) и в основном потоке программы изменяю что то
Code

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
delegate bool Delegate_Message_Processing(string input);
delegate void Delegate_Action_on_Message(Form_main form_main);
 
Delegate_Message_Processing del_mess_proc = delegate_message_processing;
Delegate_Action_on_Message del_act_mes = delegate_action_on_message;
 
IAsyncResult ar = null;
Form_main form_main = null; // её инициализировать в public Form_main() как form_main = this
static string symbolOf_COM_PORT { set; get; }
static string lineOf_COM_PORT { set; get; }
static bool lineComplete { set; get; }
static string complete_Line { set; get; }
        
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    //это действие проходит в отдельном потоке
    symbolOf_COM_PORT = serialPort1.ReadExisting();//считываем каждый символ
    lineOf_COM_PORT += symbolOf_COM_PORT;//собираем строку
 
    ar = del_mess_proc.BeginInvoke(complete_Line, null, null);
 
    //это действие проходит в отдельном потоке
    lineComplete = del_mess_proc.EndInvoke(ar);//если делегат получит правильный ответ, вернёт true
 
    //это действие проходит в ОСНОВНОМ потоке
    if (lineComplete == true)
        this.BeginInvoke(del_act_mes, form_main);//делаем какие то действия в программе
}
 
 
static bool delegate_message_processing(string text)
{
    //Console.WriteLine("Operation MESSAGE --- ThreadID {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
    if (symbolOf_COM_PORT == "\r")
    {
        if(получен правильный ответ)
        {
            complete_Line = lineOf_COM_PORT;
            return true;
        }
        lineOf_COM_PORT = string.Empty;
    }
    return false;
}
 
static void delegate_action_on_message(Form_main form_main)
{
    if (стройка)//делаем какие то действия в зависмимости от стройки
    {
        действие в программа
    }
}

Так из-за чего же я трачу ваше время?

У меня есть кнопка, нажимая на неё я создаю отдельный поток и отправляю во все моторы команду.
Но мне надо дождаться ответа перед отправлением следующей команды.

я сделал это через Sleep
Code

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
System.Threading.Thread interview = new System.Threading.Thread(interviewMotor);
 
interview.Start();//в какую нить кликабельную кнопку сую данную строчку
 
void interviewMotor()
{
    if (serialPort1.IsOpen == true)
    {
        for (int i = 0; i < maxIndex; i++)
        {
            for (int j = 1; j < 5;j++)
            {
                serialPort1.Write(arrMotor[i, j] + command_Ready);
                System.Threading.Thread.Sleep(150);
            }
        }
    } 
}

отстой, код говно!

В общем я буду благодарен вам если объясните мне как в данной ситуации:
НЕ ИСПОЛЬЗОВАТЬ SLEEP!

То есть мне надо дождаться пока delegate_message_processing закончится с true, поток продолжать цикл

пробовал держать в бесконечном цикле, пока не получу true, но не получилось)) он отправляет 1 сообщение и стопается, мб надо делегат на true посадить
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
30.06.2016, 17:04
Ответы с готовыми решениями:

Обработка данных с com порта.
При подключении к com порту контроллера, неважно какого, мне в консоль приходит от него ответ вот в таком виде: как еа рисунке. Это...

Обработка данных принятых с COM-порта
Добрыйдень опытные коллеги))) Помогите с алгоритмом обработки данных принятых с ком-порта. Есть следующее: Формат пакета от...

Чтение данных из COM порта без тормозов на форме
Здравствуйте. Не пинайте сильно, я знаю, что таких тем много на форуме, я их все(по крайней мере, что нащел) перечитал и все равно туплю. ...

17
Эксперт .NET
 Аватар для Rius
13023 / 7588 / 1661
Регистрация: 25.05.2015
Сообщений: 23,070
Записей в блоге: 14
30.06.2016, 18:09
Warsmit, Организовать приём байтов из буфера последовательного порта SerialPort
0
3 / 3 / 2
Регистрация: 24.05.2013
Сообщений: 158
01.07.2016, 20:35
Я видать не понял вопроса... Но все же.
C#
1
2
3
4
        private void DataReceivedHandler(object sender,SerialDataReceivedEventArgs e) //Скан текста на COM порте
        {
            READ = serialPort.ReadLine();
        }
Я так понимаю у тебя микроконтроллер... Данные в любом случае передаются посимвольно. Однако я получаю их отдельно строками. Разумеется и данные нужно отсылать строками. (Не знаю что там у тебя на проводе. От того и сказать не могу как это сделать)
Сам писал минипрогу для платы Ардуино
Исходники залиты в общий доступ. Там куча хлама. Но кометы есть, думаю, если это то, что нужно, ты поймешь.
https://github.com/Termir988/COM-Port
0
3 / 3 / 4
Регистрация: 13.05.2016
Сообщений: 38
20.07.2016, 21:12  [ТС]
termir988, ответ приходит по одному символу
я реализовал работу так, чтобы я собирал строку и обрабатывал её на поиск нужного ответа
ReadLine не работает, от слова вообще
0
3 / 3 / 2
Регистрация: 24.05.2013
Сообщений: 158
20.07.2016, 21:21
Цитата Сообщение от Warsmit Посмотреть сообщение
ReadLine не работает, от слова вообще
В смысле "от слова вообще". Если у вас передача идет один символ = 1 строка. То все же можно прочесть эти строки ReadLin'ом
И добавлять скажем это все в 1 строку. А дальше уже делать то что нужно.

Однако я не пойму зачем реализовывать такую передачу. Ведь как я уже и сказал передача и так пойдет по символьно. В таком же случае передача каждого будет разбавляться символом переноса строки.
Видимо я все же вас не так понял, и пожалуй помочь не могу.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
21.07.2016, 08:13
Цитата Сообщение от Warsmit Посмотреть сообщение
В общем я буду благодарен вам если объясните мне как в данной ситуации:
НЕ ИСПОЛЬЗОВАТЬ SLEEP!
Warsmit, не совсем понимаю, зачем у вас столько кода для такой простой задачи. Но попробую по порядку.
Понимаете, основная проблема, по моему опыту, в том, что порт в реальности-довольно тормозная штука. Да и он работает не сам по себе, а с каким-то устройством, что усугубляет ситуацию. Именно поэтому, ятд, использование небольшой задержки в виде Thread.Sleep(). вполне оправданно.
В своих проектах я практически никогда не использую обработчик получения данных порта. Причин несколько, например: 1. При его использовании придется каким-то образом передавать данные из обработчика во внешний код, что не всегда удобно. С учетом того, что работа с портом, как правило, осуществляется в фоновом потоке, это еще больше усложняет обработку.
2. Этот обработчик можно настроить так, чтобы он срабатывал при наличии определенного количества байт в буфере порта, но часто оно разное он одного приема к другому (это определяется протоколом общения с устройством), то есть это свойство придется постоянно менять.
А поскольку работа с к-либо устройством через порт, как правило, осуществляется по принципу запрос-ответ (и с определенным протоколом, т.е. мы всегда в определенный момент знаем, сколько байт в данный момент в ответ на команду ожидаем получить), то проще все-таки вынести работу с портом в фоновый поток, и в методе чтения крутить небольшой цикл с ожиданием получения определенного количества байт (согласно протоколу), проверяя свойство порта BytesToRead, которое показывает, сколько уже байт есть в буфере приема порта.
1
[Bicycle Reinventor]
 Аватар для Exerion
332 / 270 / 109
Регистрация: 19.10.2011
Сообщений: 668
Записей в блоге: 2
21.07.2016, 08:54
insite2012, не обязательно.
Можно сделать проще - подписаться на port_DataReceived(), в обработчике просто пушать все пришедшие байты в кольцевой буфер и тут же вызывать функцию поиска сигнатуры пакета. Если ничего не нашлось - ну и пусть, ждём нового поступления байтов; если нашлось - вычитываем из начала буфера мусор, если таковой есть, затем вычитываем сам пакет и передаём его обрабатывающему коду.
В результате нет слипа, нет вручную создаваемых потоков (только тот, что создаётся в обработчике DataReceived), схема работает при любых задержках/обрывах/мусоре в канале.
0
3 / 3 / 4
Регистрация: 13.05.2016
Сообщений: 38
21.07.2016, 09:09  [ТС]
insite2012, спасибо вам что пытаетесь мне помочь
Почему я обратил к помощи
Я работаю через виртуальный ком порт с эмулятором протеуса, он глючит на скорости выше 1200
реальное устройство будет со скоростью 19200(мб и больше)
мне нужно реализовать функцию, чтобы отправил всем моторам на устройстве команду, но я могу отправить одну команду, обязан дождаться ответа и отправить следующую команды.
Вот как раз "ждать ответа" я пытался реализовать.
Sleep(150) к примеру работал, но его значение надо будет менять на реальном устройстве + лишнее мили секунды без действия, а я хочу автоматизации.
Code

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
lineIsComplete - отвечает за получение ПРАВИЛЬНОЙ строки из COM-порта(в 1 посте описан алгоритм получения значения true)
void directionUP()
{
    if (serialPort1.IsOpen == true)
    {
        for (int i = 0; i < COUNT_DEVICE; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                waitThread = new System.Threading.Thread(wait);
Thread.Sleep(10);//даём эмулятору немного времени на отдых
                serialPort1.Write(MOTOR_FOR_DEVICE[i, j] + command_Up);//отправил команду
                waitThread.Start();//запустил поток обработки
                waitThread.Join();//жду окончания потока
            }
        }
    }
}
 
public static void wait()
{
     do
     {
         System.Threading.Thread.Sleep(1);
     } while (lineIsComplete != true);
}

Ответы у меня одного количества байт, разница только в цифре мотора, она может быть 1 а может и 10, 100 скорей всего не будет
Список команд

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//пример команды serialPort1.Write(MOTOR_FOR_SCROLLBOX[i, j] + command_Ready);
//MOTOR_FOR_SCROLLBOX[i, j] - двухмерный string массив(мне нужно именно такой :) ), в нём номера моторов
//список команд
public static readonly string command_Ready = " R\r";
public static readonly string command_Up = " U\r";
public static readonly string command_Down = " D\r";
public static readonly string command_Stop = " S\r";
//ответные статусы
public static readonly string status_direcUp = " dU\r";
public static readonly string status_direcDown = " dD\r";
public static readonly string status_isUp = " iU\r";
public static readonly string status_isDown = " iD\r";
public static readonly string status_isMedium = " iM\r";
public static readonly string status_isTimeOver = " iT\r";
public static readonly string status_isErrSwitch = " eS\r";
public static readonly string status_isErrOptocoupler = " eO\r";

Если опишите(код меня больше научит чем слова) получение строки по вашему способу, вы мне очень поможете
а так, мой костыль тоже работает))
получение строки идёт в serialPort1_DataReceived
в delegate_message_processing я обрабатываю строку из COM-порта, проверяю её на правильность(это происходит в отдельном потоке)
в delegate_action_on_message я получаю сигнал, что ПРАВИЛЬНАЯ строка получена и в поток формы делаю изменения

Простите если что то непонятно описал, еще столько делать) а сроки жмут
0
 Аватар для Sanya_sa
912 / 816 / 333
Регистрация: 03.02.2015
Сообщений: 5,276
Записей в блоге: 9
21.07.2016, 09:19
Я дата ресивд ваще не использую, а применяю метод Read() , затем выбираю те данные которые мне нужны (см код ниже) и как только разобрал что нужно (в моем случаи это пакет данных) -парсю пакет, при парсинге сохраняю выходные значения как только все эти операции закончатся генерю событие окончания чтения пакета (EndOfReading) оно является сигналом "Данные освежились - можно читать". Вот часть кода библиотеки. Удачи.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Для порта
private const int _BaudRate = 57600;
private const int _ReadTimeout = 2000;
private const int _WriteBufferSize = 2048;
private const bool _RtsEnable = true;
private const int _DataBits = 8;
 
// Буфер и его размер для чтения данных из порта
private int _Size = 399;
private byte[] _TmpBuffer = new byte[400];
 
// Текущий порт
private SerialPort _CurentPort;
 
// Завершение чтения пакета
public event EventHandler EndOfReading;
 
// Читаем наиболее актуальные данные из буфера СОМ порта
        private void ReadData()
        {
            try
            {
                _TmpBuffer = new byte[256];
                int size = _CurentPort.Read(_TmpBuffer, 0, 256/*4096*/);
 
                // Вначале оттделим команды и сохраним их в списке команд CommandList,
                // в данном списке команда с одним кодом будет в единственном экземляре
                // таким образом команда с конкретным кодом будет являться последней пришедшей с устройства, т.е. актуальной
                for (int i = 0; i < size; ++i)
                {
                    // Пытаемся поймать ответы на наши действия
                    if (i < _TmpBuffer.Length - 16)
                    {
                        if (_TmpBuffer[i] == 0xff && _TmpBuffer[i + 1] == 0xff && _TmpBuffer[i + 2] == 0x63 && _TmpBuffer[i + 4] != 0)
                        {
                            ReturnPost[0] = 0xff;
                            ReturnPost[1] = 0xff;
                            ReturnPost[2] = _TmpBuffer[i + 4];
                            ReturnPost[3] = _TmpBuffer[i + 5];
                            ReturnPost[4] = _TmpBuffer[i + 6];
                            ReturnPost[5] = _TmpBuffer[i + 7];
                            ReturnPost[6] = _TmpBuffer[i + 8];
                            ReturnPost[7] = _TmpBuffer[i + 9];
                            ReturnPost[8] = _TmpBuffer[i + 10];
                            ReturnPost[9] = _TmpBuffer[i + 11];
                            ReturnPost[10] = _TmpBuffer[i + 12];
                            ReturnPost[11] = _TmpBuffer[i + 13];
                        }
 
                        if (_TmpBuffer[i] == 0xff && _TmpBuffer[i + 1] == 0xff && _TmpBuffer[i + 2] == 0x64 && _TmpBuffer[i + 4] != 0)
                        {
                            ReturnPost[12] = _TmpBuffer[i + 4];
                            ReturnPost[13] = _TmpBuffer[i + 5];
                            ReturnPost[14] = _TmpBuffer[i + 6];
                            ReturnPost[15] = _TmpBuffer[i + 7];
                            ReturnPost[16] = _TmpBuffer[i + 8];
                            ReturnPost[17] = _TmpBuffer[i + 9];
 
                            // Событие отклик на наши действия
                            if (ResponseOnComand != null) ResponseOnComand(this, ReturnPost);
                        }
                    }
 
                    // Получен маркер нового пакета данных в то время как предыдущий пакет не был дополучен 
                    // (обеспечивается условием DataBufferSize > 1)
                    if (_TmpBuffer[i] == 0xff && DataBufferSize > 1 && DataBuffer[DataBufferSize - 1] == 0xff)
                    {
                        // Обнулим буффер данных от устройства
                        DataBuffer[0] = 0xff;
                        DataBufferSize = 1;
                    }
 
                    DataBuffer[DataBufferSize] = _TmpBuffer[i];
                    ++DataBufferSize;
 
                    // Пакеты данных всегда по MAX_DATA_SIZE байт
                    if (DataBufferSize >= MAX_DATA_SIZE)
                    {
                        AddCommand(DataBuffer, DataBufferSize);
 
                        // После разбора данных обнулим буффер
                        DataBufferSize = 0;
                    }
                }
 
                // После получения всех команд обработаем  все данные пришедшие в _актуальных_ командах
                IEnumerator<KeyValuePair<byte, byte[]>> enumerator = CommandList.GetEnumerator();
 
                while (enumerator.MoveNext())
                {
                    KeyValuePair<byte, byte[]> item = enumerator.Current;
                    ParseData(item.Value, item.Value.Length);
 
                    // Событие окончания
                    if (EndOfReading != null) EndOfReading(this, EventArgs.Empty);
                }
                enumerator = null;
                CommandList.Clear();
            }
            catch (Exception)
            {
                _On = false;
            }
        }
1
Эксперт .NET
 Аватар для Rius
13023 / 7588 / 1661
Регистрация: 25.05.2015
Сообщений: 23,070
Записей в блоге: 14
21.07.2016, 09:22
Опишу подробнее код из 2 поста.
Работал на 57600, с временем ответа от устройства в районе 1мс.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/// <summary>
/// Отправка/приём данных через SerialPort.
/// </summary>
/// <param name="connection">SerialPort, используемый для связи.</param>
/// <param name="sendBuffer">Массив отправляемых байт.</param>
/// <param name="waitingForStart">Время ожидания от отправки до начала ответа.</param>
/// <param name="waitForEnd">Время ожидания после завершения ответа.</param>
/// <param name="cancellationToken">Токен отмены.</param>
/// <param name="recvBuffer">Массив буфера для приёма байт.</param>
/// <param name="received">Количество принятых байт.</param>
/// <returns>Получен ли какой-нибудь ответ.</returns>
public bool SendRecv(SerialPort connection, byte[] sendBuffer, int waitingForStart, int waitForEnd, CancellationToken cancellationToken, ref byte[] recvBuffer, out int received)
{
    bool result = false;
    received = -1;
 
    try
    {
        // Порт должен быть открыт.
        if (connection.IsOpen)
        {
            // Сброс буферов.
            connection.DiscardInBuffer();
            connection.DiscardOutBuffer();
 
            if (connection.BytesToRead > 0)
            {
                connection.ReadExisting();
            }
 
            // Установка таймаута начала ответа.
            connection.ReadTimeout = waitingForStart;
 
            // Отправка данных.
            connection.Write(sendBuffer, 0, sendBuffer.Length);
 
            int readed = 0;
 
            try
            {
                // Ожидание первого байта. Если ничего не придёт за waitingForStart мс, то вылетит TimeoutException.
                recvBuffer[readed++] = Convert.ToByte(connection.ReadByte());
 
                // Установка таймаута для определения завершения ответа.
                connection.ReadTimeout = waitForEnd;
 
                try
                {
                    // Временный буфер для приёма частями.
                    byte[] temp = new byte[16];
 
                    do
                    {
                        // Чтение и заполнение буфера.
                        // Если ничего не принято, то вылетит TimeoutException.
                        // Если считано меньше или равно размеру буфера, это число возвращается функцией.
                        int b = connection.Read(temp, 0, 16);
 
                        // Копирование в выходной буфер.
                        for (int i = 0; i < b; i++)
                        {
                            recvBuffer[readed++] = temp[i];
                        }
 
                        // Повтор до явной отмены.
                        // Также, выход доступен по таймауту чтения.
                    } while (!cancellationToken.IsCancellationRequested);
                }
                catch (TimeoutException)
                {
                }
                finally
                {
                    // Количество считанного.
                    received = readed;
                    // Возвращаемый результат: принято ли что-нибудь.
                    result = readed > 0;
                }
            }
            catch (TimeoutException)
            {
            }
        }
    }
    catch
    {
        result = false;
    }
 
    return result;
}
1
484 / 397 / 68
Регистрация: 14.02.2014
Сообщений: 1,930
21.07.2016, 09:44
) ну раз пошли меряться методами приёма данных из COM порта, то и я в стороне не останусь

здесь протокол обмена Modbus RTU. Алгоритм обмена такой: послали команду и начинаем ждать ответ в течение определённого периода времени, всё, что пришло в порт в это время проверяем на пустоту и складываем в общий буфер. По истечении таймаута сверяем контрольную сумму. Если всё зашибись - отдаём наверх весь пакет, если нет - сообщаем какая беда приключилась.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
        void myComPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {           
            SerialPort myPort = (SerialPort)sender;
            int needBytes = myPort.BytesToRead;     // определяем количество ожидаемых байт
            if (needBytes <= 0)
                return;
            byte[] newData = new byte[needBytes];   // выделяем буфер под полученные байты
            int readBytes = 0;
            while (readBytes != needBytes) // пока количество прочитанных байт не сравняется с количеством ожидаемых...
            {
                changeSignalEvent(Signals.RX, true); // зажигаем индикатор RX
                Thread.Sleep(30); // здесь слип нужен только для того, чтобы в интерфейсе мигание индикатора было заметно
                // считываем данные из порта в массив newData
                // начиная с позиции readBytes
                // количество читаемых байт needBytes - readBytes (количество ожидаемых минус количество считанных)
                // количество считанных данным методом байт прибавляем к readBytes
                readBytes += myPort.Read(newData, readBytes, needBytes - readBytes);
            }
            changeSignalEvent(Signals.RX, false); // гасим индикатор RX
            bool isNullData = true;
            for (int i = 0; i < needBytes; i++)
            {
                if (newData[i] != 0) // обходим багу, когда вместо ответа одни нули
                {
                    isNullData = false;
                    break;
                }
            }
            if (isNullData)
            {
                newDataEvent(MdbResult.EMPTY, inData.ToArray());
                timeout.Stop();
                return;
            }
            inData.AddRange(newData); // прибавляем полученные байты к общему буферу ответа          
        }
1
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
21.07.2016, 11:01
Цитата Сообщение от aquaMakc Посмотреть сообщение
ну раз пошли меряться методами приёма данных из COM порта, то и я в стороне не останусь
Аналогично. Код ниже (с использованием стороннего железа как адаптера) используется для подбора значения векторов (используемых как код для возможности чтения) в мк Motorola, серия HC08.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.ComponentModel;
using System.IO.Ports;
 
using sqlitedb.Models;
                          
namespace MON08_Code_Searcher {
    /// <summary>
    /// Класс-генератор для перебора всех кодов, предоставленных алгоритмом поиска
    /// </summary>
    public class CodeGenerator : BackgroundWorker {
 
        SerialPort _port = null; //Поле последовательного порта
        IAlgorithm _algo = null; //Поле алгоритма поиска
        uint _pause = 0;         //Поле задержки между запросами
        TargetModel _target;      //Тип устройства
        public string CurrentCode { get; private set; }  //Автосвойство, текущий код 
 
        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="algorythm">Алгоритм поиска</param>
        /// <param name="target">Модель выбранного мк</param>
        /// <param name="portName">Номер последовательного порта</param>
        /// <param name="baudRate">Скорость последовательного порта</param>
        public CodeGenerator(IAlgorithm algorythm, TargetModel target, string portName, int baudRate) {
            _port = new SerialPort {
                PortName = portName,
                BaudRate = baudRate
            };
            _algo = algorythm;
            _pause = target.Pause;
            _target = target;
 
            this.WorkerReportsProgress = true;
            this.WorkerSupportsCancellation = true;
        }
 
        /// <summary>
        /// Переопределенный метод OnDoWork()
        /// </summary>
        /// <param name="e"></param>
        protected override void OnDoWork(DoWorkEventArgs e) {
            using (_port) {
                _port.Open();
                StartSearch(e);
            }
        }
 
        /// <summary>
        /// Основной метод поиска кода
        /// </summary>
        /// <param name="e"></param>
        void StartSearch(DoWorkEventArgs e) {
            //Событие старта поиска, с общим размером текущего диапазона
            this.ReportProgress(0, ProgressState.START);
            //Цикл поиска
            while (_algo.NextResult()) {
                //Проверка признака отмены операции
                if (this.CancellationPending) {
                    e.Cancel = true;
                    //Событие отмены операции поиска
                    this.ReportProgress(0, ProgressState.CANCELED);
                    return;
                }
 
                //Массив байт кода запроса
                byte[] code = Convert(_algo.Current);
                //Установка текущего кода из диапазона
                CurrentCode = _algo.Current.ToString();
 
                //Событие шага прогресса
                this.ReportProgress(0, ProgressState.STEP);
 
                //Включение DTR порта
                _port.DtrEnable = true;
 
                //Векторный запрос
                if (SendVectorAsc(code)) {
                    //Адресный запрос
                    if (SendAddressAsc(code)) {
                        e.Result = true;
                        return;
                    }
                }
                //Отключение DTR порта
                _port.DtrEnable = false;
                //Задержка между запросами
                Thread.Sleep((int)_target.Pause);
            }
            //Событие окончания поиска
            e.Result = false;
            this.ReportProgress(0, ProgressState.END);
        }
 
        /// <summary>
        /// Векторный запрос
        /// </summary>
        /// <param name="vector">Вектор для запроса</param>
        /// <returns>Результат запроса</returns>
        bool SendVectorAsc(byte[] vector) {
            for (int i = 0; i < vector.Count(); i++) {
                int count = i != vector.Count() - 1 ? 2 : 3;
                ReadBlock(vector[i], count, null);
            }
            return true;
        }
        
        /// <summary>
        /// Адресный запрос
        /// </summary>
        /// <param name="vector">Вектор для адресного запроса</param>
        /// <returns>Результат запроса</returns>
        bool SendAddressAsc(byte[] vector) {
            byte readCommand = 0x4A;
            byte vectorAddress = 0xF6;
            List<byte> temp = new List<byte>();
 
            for (int i = 0; i < vector.Count(); i++) {
                ReadBlock(readCommand, 2, null);
                ReadBlock(0xFF, 2, null);
                ReadBlock((byte)(vectorAddress + i), 3, temp);
                if (temp[2] != vector[i]) {
                    return false;
                }
            }
            return true;
        }
 
        /// <summary>
        /// Метод записи/чтения блока данных из последовательного порта
        /// </summary>
        /// <param name="code">Блок данных для записи в порт</param>
        /// <param name="readCount">Количество байт читаемых из порта</param>
        /// <param name="temp">Маасив данных для считанных данных</param>
        void ReadBlock(byte code, int readCount, List<byte> temp) {
            for (int i = 0; i < 10; i++) {
                _port.DiscardInBuffer();
                _port.Write(new byte[] { code }, 0, 1);
                for (int j = 0; j < 10; j++) {
                    if (_port.BytesToRead == readCount) {
                        byte[] data = new byte[readCount];
                        _port.Read(data, 0, readCount);
                        if (temp != null) {
                            temp.Clear();
                            temp.AddRange(data);
                        }
                        return;
                    }
                    else {
                        Thread.Sleep(100);
                    }
                }
            }
            throw new TimeoutException("NO ANSWER FROM DEVICE");
        }
 
        /// <summary>
        /// Метод создания кода запроса (Vector >>> byte[])
        /// </summary>
        /// <param name="vector">Вектор запроса</param>
        /// <returns>Маасив байт для запроса</returns>
        byte[] Convert(Vector4 vector) {
            List<byte> temp = new List<byte>();
            for (int i = 0; i < vector.Count; i++) {
                temp.Add((byte)(vector[i] / 0x100));
                temp.Add((byte)(vector[i] & 0xFF));
            }
            return temp.ToArray();
        }
 
        /// <summary>
        /// Метод проверки корректности установленных диапазонов
        /// </summary>
        /// <param name="code">Массив ushort запроса в порт</param>
        /// <returns>Результат проверки корректности</returns>
        bool IsRangeCorrected(ushort[] code) {
            if (code.All((c) => c >= _target.Memory.Start & c <= _target.Memory.End)) {
                return true;
            }
            return false;
        }
    }
 
    /// <summary>
    /// Перечисление этапов прогресса оперции
    /// </summary>
    internal enum ProgressState {
        START,
        END,
        STEP,
        CANCELED
    } 
}
1
Эксперт .NET
 Аватар для Rius
13023 / 7588 / 1661
Регистрация: 25.05.2015
Сообщений: 23,070
Записей в блоге: 14
21.07.2016, 11:10
А кто-нибудь делал с асинхронным чтением потока?
В If you *must* use .NET System.IO.Ports.SerialPort автор утверждает, что это правильный путь.
0
484 / 397 / 68
Регистрация: 14.02.2014
Сообщений: 1,930
21.07.2016, 11:19
Цитата Сообщение от Rius Посмотреть сообщение
автор утверждает, что это правильный путь.
Из статьи:
Starting in .NET 4.5, you can instead call ReadAsync on the BaseStream object, which calls BeginRead and EndRead internally.
И пишу исключительно на 4.0, т.к. у наших клиентов ещё дохрена XP`шек. Работа с портом всё-равно выносится в отдельный поток, асинхронные методы наверняка упростили бы код.

Добавлено через 4 минуты
Какую-то фигню сморозил ).
Поток с асинхронными методами и в ранних версиях .Net получить можно. Надо будет попробовать как-нибудь.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
21.07.2016, 11:29
Цитата Сообщение от aquaMakc Посмотреть сообщение
асинхронные методы наверняка упростили бы код.
aquaMakc, не пробовали использовать Async CTP Library? Она дает для NET 4.0 то, что есть в NET 4.5, в плане асинхронности. Естественно, к приложению прибавится одна dll.
0
484 / 397 / 68
Регистрация: 14.02.2014
Сообщений: 1,930
21.07.2016, 11:35
insite2012, не вижу смысла. Там особо ничего существенно нужного для меня не добавляется. async-await разве-что, но вполне успешно обхожусь BackGroundWorker`ом и Task`ами. Плюс асинхронные методы тех-же IO потоков и сокетов. А есть ещё PLINQ, которому я как-то у себя не нашёл применения.
0
Эксперт .NET
 Аватар для Rius
13023 / 7588 / 1661
Регистрация: 25.05.2015
Сообщений: 23,070
Записей в блоге: 14
09.09.2016, 10:56
Цитата Сообщение от Rius Посмотреть сообщение
А кто-нибудь делал с асинхронным чтением потока?
В If you *must* use .NET System.IO.Ports.SerialPort автор утверждает, что это правильный путь.
Получилось что-то такое:

Таймауты
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using System;
using System.Threading;
 
namespace SerialStreamTest
{
    /// <summary>
    /// Таймер отсчёта таймаута
    /// </summary>
    class TimerTimeout
    {
        private Timer mTimer;
        private Action mCallback;
        private object mCallbackLocker;
 
        public TimerTimeout()
        {
            this.mTimer = new Timer(this.CallBack, null, Timeout.Infinite, Timeout.Infinite);
            this.mCallback = null;
            this.mCallbackLocker = new object();
        }
 
        private void CallBack(object state)
        {
            lock (this.mCallbackLocker)
            {
                if (this.mCallback != null)
                {
                    this.mCallback();
                }
            }
        }
 
        /// <summary>
        /// Запуск/перезапуск таймера.
        /// <paramref name="Callback"/> будет вызван через <paramref name="millisecondsTimeout"/> мс после текущего момента времени.
        /// Если таймер был запущен ранее, предыдущий <paramref name="Callback"/> отменится.
        /// </summary>
        /// <param name="millisecondsTimeout">Время, через которое будет вызван <paramref name="Callback"/>.</param>
        /// <param name="callback">Функция обратного вызова по срабатыванию таймера.</param>
        public void ReStart(int millisecondsTimeout, Action callback)
        {
            lock (this.mCallbackLocker)
            {
                this.mCallback = callback;
            }
 
            this.mTimer.Change(millisecondsTimeout, Timeout.Infinite);
        }
 
        /// <summary>
        /// Отмена отсчёта таймера.
        /// </summary>
        public void Stop()
        {
            this.mTimer.Change(Timeout.Infinite, Timeout.Infinite);
 
            lock (this.mCallbackLocker)
            {
                this.mCallback = null;
            }
        }
    }
}


Сам класс для обмена
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
 
namespace SerialStreamTest
{
    /// <summary>
    /// Класс чтения/записи в последовательный порт.
    /// </summary>
    /// <remarks>
    /// Задача: записать данные в порт и принять ответ.
    /// Ответа может и не быть.
    /// Отдельные таймауты на ожидание начала приёма и на ожидание завершения приёма.
    /// 
    /// Запись работает как обычно.
    /// 
    /// Приём:
    /// Запускаем таймер отсчёта начала ответа.
    /// Входим в состояние приёма.
    /// Выход из него возможен по таймауту начала ответа и по таймауту завершения приёма.
    /// Если время начала ответа вышло, ожидание завершается и возвращается пустой массив.
    /// Если за время начала ответа этот ответ начал приниматься, ждём уже завершения приёма.
    /// </remarks>
    class SerialStream4 : IDisposable
    {
        protected SerialPort mPort;
 
        protected ConcurrentQueue<byte> mReceiveQueue;
        /// <summary>
        /// Задача для повторяющегося чтения
        /// </summary>
        protected Task mReaderTask;
        /// <summary>
        /// Токен отмены задачи повторяющегося чтения
        /// </summary>
        protected CancellationTokenSource mReaderTaskToken;
        /// <summary>
        /// Событие приёма чего-либо
        /// </summary>
        protected ManualResetEventSlim mReadEvent;
        /// <summary>
        /// Событие таймаута начала приёма
        /// </summary>
        protected ManualResetEventSlim mStartTimeoutEvent;
        /// <summary>
        /// Событие таймаута после приёма
        /// </summary>
        protected ManualResetEventSlim mEndTimeoutEvent;
        /// <summary>
        /// Таймер для отсчёта таймаута начала пакета
        /// </summary>
        protected TimerTimeout mTimerStartTimeout;
        /// <summary>
        /// Таймер для отсчёта таймаута завершения пакета
        /// </summary>
        protected TimerTimeout mTimerEndTimeout;
        /// <summary>
        /// Время ожидания завершения приёма, мс
        /// </summary>
        protected int mEndTimeout;
 
        public SerialStream4()
        {
            this.mPort = new SerialPort();
            this.mReceiveQueue = new ConcurrentQueue<byte>();
            this.mReadEvent = new ManualResetEventSlim(false);
            this.mStartTimeoutEvent = new ManualResetEventSlim(false);
            this.mEndTimeoutEvent = new ManualResetEventSlim(false);
            this.mTimerStartTimeout = new TimerTimeout();
            this.mTimerEndTimeout = new TimerTimeout();
            this.mEndTimeout = Timeout.Infinite;
        }
 
        public void Dispose()
        {
            this.Close();
        }
 
        public bool Open(string portName, int baudRate, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One)
        {
            this.mPort.PortName = portName;
            this.mPort.BaudRate = baudRate;
            this.mPort.Parity = parity;
            this.mPort.DataBits = dataBits;
            this.mPort.StopBits = stopBits;
            this.mPort.ReadTimeout = 100;
 
            this.mPort.Open();
 
            if (this.mPort.IsOpen)
            {
                this.mReaderTaskToken = new CancellationTokenSource();
                this.mReaderTask = Task.Factory.StartNew(o => this.AsyncReader((CancellationToken)o), this.mReaderTaskToken.Token, this.mReaderTaskToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
            }
 
            return this.mPort.IsOpen;
        }
 
        private void AsyncReader(CancellationToken token)
        {
            while (true)
            {
                Task<ReadedData> task = this.ReadPart(64, 100, this.mReaderTaskToken.Token);
 
                task.Wait(token);
                token.ThrowIfCancellationRequested();
 
                foreach (var b in task.Result.Array.Take(task.Result.Count))
                {
                    this.mReceiveQueue.Enqueue(b);
                }
 
                this.mReadEvent.Set();
            }
        }
 
        public void Close()
        {
            if (this.mPort.IsOpen)
            {
                try
                {
                    this.mReaderTaskToken.Cancel();
                    this.mReaderTask.Wait();
                }
                catch (AggregateException)
                {
                }
            }
 
            this.mPort.Close();
        }
 
        public bool IsOpen
        {
            get { return this.mPort.IsOpen; }
        }
 
        public void DiscardInBuffer()
        {
            byte b;
 
            while (this.mReceiveQueue.TryDequeue(out b)) ;
 
            this.mReadEvent.Reset();
        }
 
        public void Write(byte[] buffer)
        {
            Console.WriteLine("Send");
            this.mPort.Write(buffer, 0, buffer.Length);
        }
 
        public bool Read(out byte[] receivedBuffer, int startTimeout, int endTimeout, CancellationToken cancellationToken)
        {
            this.mStartTimeoutEvent.Reset();
            this.mEndTimeoutEvent.Reset();
 
            this.mEndTimeout = endTimeout;
 
            //this.mTimerStartTimeout.ReStart(startTimeout, this.mStartTimeoutEvent.Set);
            this.mTimerStartTimeout.ReStart(startTimeout, this.StartTimeoutCallback);
 
            int completedIndex = WaitHandle.WaitAny(
                new WaitHandle[] {
                    this.mEndTimeoutEvent.WaitHandle,
                    this.mStartTimeoutEvent.WaitHandle,
                    cancellationToken.WaitHandle
                });
 
            switch (completedIndex)
            {
                case 0:
                    {
                        this.mEndTimeoutEvent.Reset();
                        this.mStartTimeoutEvent.Reset();
 
                        List<byte> bytes = new List<byte>();
                        byte b;
 
                        while (this.mReceiveQueue.TryDequeue(out b))
                        {
                            bytes.Add(b);
                        }
 
                        receivedBuffer = bytes.ToArray();
 
                        return true;
                    }
                case 1:
                case 2:
                default:
                    {
                        this.mEndTimeoutEvent.Reset();
                        this.mStartTimeoutEvent.Reset();
 
                        receivedBuffer = new byte[] { };
 
                        return false;
                    }
            }
        }
 
        private Task<ReadedData> ReadPart(int expectedCount, int millisecondsTimeout, CancellationToken cancellationToken)
        {
            ReadedData data = new ReadedData();
 
            List<byte> buffer = new List<byte>();
            byte[] tempBuffer = new byte[expectedCount];
 
            data.Array = tempBuffer;
 
            Task<int> taskReader = Task<int>.Factory.FromAsync(
                this.mPort.BaseStream.BeginRead, // IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state);
                this.mPort.BaseStream.EndRead, // int EndRead(IAsyncResult asyncResult);
                tempBuffer,
                0,
                tempBuffer.Length,
                data);
 
            Task<ReadedData> taskContinuation = taskReader.ContinueWith<ReadedData>(x =>
            {
                int actualLength = 0;
 
                try
                {
                    actualLength = x.Result;
                    data.Count = actualLength;
                    this.mTimerStartTimeout.Stop();
 
                    //this.mTimerEndTimeout.ReStart(this.mEndTimeout, this.mEndTimeoutEvent.Set);
                    this.mTimerEndTimeout.ReStart(this.mEndTimeout, this.EndTimeoutCallback);
 
                    Console.WriteLine("Received: [{0}]", actualLength);
                }
                catch (IOException exc)
                {
                    Console.WriteLine(exc.Message);
                    data.Exception = exc;
                }
 
                return data;
            });
 
            return taskContinuation;
        }
 
        private void StartTimeoutCallback()
        {
            Console.WriteLine("Answer not received");
            this.mStartTimeoutEvent.Set();
        }
 
        private void EndTimeoutCallback()
        {
            Console.WriteLine("Answer completed");
            this.mEndTimeoutEvent.Set();
        }
 
        private class ReadedData
        {
            public byte[] Array { get; set; }
            public int Count { get; set; }
            public Exception Exception { get; set; }
 
            public ReadedData()
            {
                this.Array = new byte[] { };
                this.Count = 0;
                this.Exception = null;
            }
        }
    }
}


Пример
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System;
using System.IO.Ports;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
 
namespace SerialStreamTest
{
    class Program
    {
        static void Main(string[] args)
        {
            CancellationTokenSource token = new CancellationTokenSource();
 
            Task task = Task.Factory.StartNew(o => Read((CancellationToken)o), token.Token, token.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
 
            Console.ReadLine();
 
            try
            {
                token.Cancel();
                task.Wait();
            }
            catch (AggregateException)
            {
            }
        }
 
        private static void Read(CancellationToken token)
        {
            byte[] send = new byte[] { ... тут байты запроса ... };
            using (var port = new SerialStream4())
            {
                port.Open("COM28", 19200, Parity.Odd, 8, StopBits.One);
 
                while (!token.IsCancellationRequested)
                {
                    port.Write(send);
 
                    byte[] recv;
 
                    if (port.Read(out recv, 100, 4, token))
                    {
                        Console.WriteLine("[{0}] {1}", recv.Length, String.Join(" ", recv.Select(x => x.ToString("X2"))));
                    }
                }
 
                port.Close();
            }
        }
    }
}
0
 Аватар для Ljubin
0 / 0 / 0
Регистрация: 11.08.2012
Сообщений: 18
04.05.2020, 20:14
Exerion: Пример кода можно... - если не затруднит.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
04.05.2020, 20:14
Помогаю со студенческими работами здесь

Как сделать паузу без подвисания и без вызова Sleep?
Здравствуйте, как можно сделать паузу без &quot;второго&quot; потока и вешания программы посредством System.Threading.Thread.Sleep? ...

Команда LOOP. Обработка данных в цикле (без использования ветвлений)
нужно решить задачу под номером 5. 4. Дана последовательность заглавных и строчных латинских букв (строка текста), размещённая...

Задержка функции вывода без использования Thread.sleep
Доброе время суток всем. Прошу о помощи Вас ) Возможно ли вывести сообщение с задержкой без использования Thread.sleep? Data now...

Обработка данных из порта
Добрый день. Данные с порта считываются каждую секунду. Эти данные надо обработать. Вопрос как это сделать ? А то начинаю орабатывать тут: ...

Обработка данных с COM порта
ПОдскажите, как обрабатывать строку полученную с COm порта У меня с COM порта идет строка ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
18
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru