Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.67/3: Рейтинг темы: голосов - 3, средняя оценка - 4.67
32 / 15 / 5
Регистрация: 19.11.2014
Сообщений: 626

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

08.06.2017, 09:03. Показов 723. Ответов 7
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Написал класс для работы с устройствами по протоколу Modbus TCP. Читаю регистры (один регистр это два байта) 9999 регистров моя программа читает более секунды, это не учитывая, что полученный результат нужно складывать в массив отдельный. По сути же, она должна в пару десятков миллисекунд отрабатывать.
Program.cs
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static void Main(string[] args)
        {
            try
            {
                ModbusTCP device = new ModbusTCP();
                device.Connect(502, 1, "192.168.1.21", 1, true, 1000);
 
                ushort[] registers = new ushort[9999];
 
                DateTime start = DateTime.Now;
                for (int i = 0; i < 80; i++)
                {
                    device.ReadUsignedHoldingRegisters((ushort)(40001 + i * 125), 125);
                }
                DateTime finish = DateTime.Now;
 
                Console.WriteLine(finish - start);
                Console.ReadKey();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
ModbusTCP.cs
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
public class ModbusTCP
    {
        private int port;
        private byte modbusStation;
        private string ipAddress;
        private ushort shift;    //отсчет регистров с 0 или с 1
        private bool byteOrder;  //порядок байт
 
        public Socket Socket { get; set; }
 
        public ModbusTCP() { }
 
        public void Connect(int _port, byte _modbusStation, string _ipAddress, ushort _shift, bool _byteOrder, int timeout)
        {
            try
            {
                port = _port;
                modbusStation = _modbusStation;
                shift = _shift;
                byteOrder = _byteOrder;
                ipAddress = _ipAddress;
                IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
                Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                Socket.ReceiveTimeout = timeout;
                Socket.Connect(ipPoint);
            }
            catch
            {
                
            }
        }
 
        public ushort[] ReadUsignedHoldingRegisters(ushort firstRegister, ushort amountRegisters)
        {
            if (firstRegister >= 40000 && firstRegister < 50000) firstRegister -= 40000;
            byte[] dataReceive = SendReceive(CreateDataForReadHoldingRegisters(firstRegister, amountRegisters));
            ushort[] dataUshort = new ushort[amountRegisters];
            for (int i = 0; i < amountRegisters; i++)
            {
                byte[] byteTemp = new byte[2];
                if (byteOrder) byteTemp = new byte[2] { dataReceive[i * 2 + 10], dataReceive[i * 2 + 9] };
                else byteTemp = new byte[2] { dataReceive[i * 2 + 9], dataReceive[i * 2 + 10] };
                dataUshort[i] = BitConverter.ToUInt16(byteTemp, 0);
            }
            return dataUshort;
        }
 
        private List<byte[]> CreateDataForReadHoldingRegisters(ushort firstRegister, ushort amountRegisters)
        {
            byte[] dataSend = new byte[12]; //запрос на чтение
 
            byte[] dataReceive = new byte[9 + amountRegisters * 2]; //ответ от устройства
            firstRegister -= shift; //адресс первого регистра в соответствии со сдвигом
 
            //Формирую запрос
 
            //идентификатор транзакции
            dataSend[0] = 30;
            dataSend[1] = 0;
            //идентификатор протокола
            dataSend[2] = 0;
            dataSend[3] = 0;
            //длина сообщения
            dataSend[4] = 0;
            dataSend[5] = 6;
            //адрес устройства
            dataSend[6] = modbusStation;
            //функция
            dataSend[7] = 3;
            //адрес первого регистра
            dataSend[8] = BitConverter.GetBytes(firstRegister)[1];
            dataSend[9] = BitConverter.GetBytes(firstRegister)[0];
            //количество необходимых регистров
            dataSend[10] = BitConverter.GetBytes(amountRegisters)[1];
            dataSend[11] = BitConverter.GetBytes(amountRegisters)[0];
 
            return new List<byte[]>() { dataSend, dataReceive };
        }
 
        private byte[] SendReceive(List<byte[]> data)
        {
            Socket.Send(data[0]);
            Socket.Receive(data[1]);
            return data[1];
        }
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
08.06.2017, 09:03
Ответы с готовыми решениями:

Нужна критика по оптимизации, продвижению сайта
Здравствуйте! Мне интересна хорошая критика, взгляд со стороны на сайт ukrcomcentr.com Корпоративный сайт, посвящен направлениям...

Нужна критика и совет
Посмотрите если не трудно сайт Оперативная полиграфия, что можете сказать, хочу выслушать Вашу критику, а может кто советом поможет или...

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

7
484 / 397 / 68
Регистрация: 14.02.2014
Сообщений: 1,930
08.06.2017, 11:57
Вместо
C#
1
2
3
DateTime start = DateTime.Now;
...
DateTime finish = DateTime.Now
лучше использовать Stopwatch

дважды инициализируется массив:
C#
1
2
3
byte[] byteTemp = new byte[2];
if (byteOrder) byteTemp = new byte[2] { dataReceive[i * 2 + 10], dataReceive[i * 2 + 9] };
else byteTemp = new byte[2] { dataReceive[i * 2 + 9], dataReceive[i * 2 + 10] };
можно вообще в одну строчку:
C#
1
byte[] byteTemp1 = new byte[2] { dataReceive[i * 2 +(byteOrder ? 10 : 9)], dataReceive[i * 2 + (byteOrder ? 9 : 10)] };
тут я бы через битовые операции сделал:
C#
1
BitConverter.GetBytes(firstRegister)[1];
1
32 / 15 / 5
Регистрация: 19.11.2014
Сообщений: 626
13.06.2017, 10:51  [ТС]
Цитата Сообщение от aquaMakc Посмотреть сообщение
лучше использовать Stopwatch
дважды инициализируется массив:
Все равно полторы секунды выполняется.
Цитата Сообщение от aquaMakc Посмотреть сообщение
тут я бы через битовые операции сделал:
Для ускорения работы или какие то другие причины?
0
484 / 397 / 68
Регистрация: 14.02.2014
Сообщений: 1,930
13.06.2017, 11:33
Цитата Сообщение от Chizel Посмотреть сообщение
Все равно полторы секунды выполняется.
У вас, скорее всего, основное время уходит на выполнение метода SendReceive, а особенно - ожидание ответа от клиента. Чтобы этот процесс ускорить - надо переделывать логику программы на многопоточность, будь то отдельные потоки, распараллеливание PLINQ или асинхронные альтернативы.

Добавлено через 1 минуту
Цитата Сообщение от Chizel Посмотреть сообщение
Для ускорения работы или какие то другие причины?
Если ускорение и будет, то вряд ли вы его заметите без бенчмарка. Просто, это более наглядный механизм.
1
32 / 15 / 5
Регистрация: 19.11.2014
Сообщений: 626
13.06.2017, 15:29  [ТС]
Цитата Сообщение от aquaMakc Посмотреть сообщение
Чтобы этот процесс ускорить - надо переделывать логику программы на многопоточность, будь то отдельные потоки, распараллеливание PLINQ или асинхронные альтернативы.
реализовал вот так, все равно полторы секунды
Program.cs
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
                ModbusTCP scadapack330 = new ModbusTCP();
                scadapack330.Connect(502, 1, "192.168.1.21", 1, true, 1000);
 
                ushort[] registers = new ushort[9999];
                Task[] tasks = new Task[80];
                Stopwatch sw = Stopwatch.StartNew();
 
                for (int i = 0; i < 80; i++)
                {
                    int a = i;
                    tasks[i] = Task.Factory.StartNew(() => scadapack330.Read((ushort)(40001 + a * 125), 125));
                }
                Task.WaitAll(tasks);
                sw.Stop();
                Console.WriteLine("=======");
                Console.WriteLine(sw.ElapsedMilliseconds);
                Console.ReadKey();
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
        public void Read(ushort firstRegister, ushort amountRegisters)
        {
            if (firstRegister >= 40000 && firstRegister < 50000) firstRegister -= 40000;
            byte[] dataSend = new byte[12]; //запрос на чтение
 
            byte[] dataReceive = new byte[9 + amountRegisters * 2]; //ответ от устройства
            firstRegister -= shift; //адресс первого регистра в соответствии со сдвигом
 
            //Формирую запрос
            identifierTranzaction++;
            //идентификатор транзакции
            dataSend[0] = BitConverter.GetBytes(identifierTranzaction)[1];
            dataSend[1] = BitConverter.GetBytes(identifierTranzaction)[0];
            //идентификатор протокола
            dataSend[2] = 0;
            dataSend[3] = 0;
            //длина сообщения
            dataSend[4] = 0;
            dataSend[5] = 6;
            //адрес устройства
            dataSend[6] = modbusStation;
            //функция
            dataSend[7] = 3;
            //адрес первого регистра
            dataSend[8] = BitConverter.GetBytes(firstRegister)[1];
            dataSend[9] = BitConverter.GetBytes(firstRegister)[0];
            //количество необходимых регистров
            dataSend[10] = BitConverter.GetBytes(amountRegisters)[1];
            dataSend[11] = BitConverter.GetBytes(amountRegisters)[0];
 
            Socket.Send(dataSend);
            Socket.Receive(dataReceive);
 
            ushort[] dataUshort = new ushort[amountRegisters];
            for (int i = 0; i < amountRegisters; i++)
            {
                byte[] byteTemp = new byte[2] { dataReceive[i * 2 + (byteOrder ? 10 : 9)], dataReceive[i * 2 + (byteOrder ? 9 : 10)] };
                dataUshort[i] = BitConverter.ToUInt16(byteTemp, 0);
            }
 
        }
0
484 / 397 / 68
Регистрация: 14.02.2014
Сообщений: 1,930
13.06.2017, 15:38
Само-собой.
Ты один раз создал ModbusTCP, соответственно Socket тоже один. И ты через 1 сокет синхронно пытаешься отправить данные и прочесть ответ. То, что это делаешь в разных потоках никак не влияет на суть процесса. Более того, в таком варианте НИКТО не может гарантировать адекватность работы программы в целом.
Я так понял, что ты "долбишь" одно и то-же устройство читая 80 разных регистров. Оно врядли работает асинхронно. Тут без вариантов - смирись и опрашивай регистры последовательно.
1
32 / 15 / 5
Регистрация: 19.11.2014
Сообщений: 626
13.06.2017, 16:51  [ТС]
Цитата Сообщение от aquaMakc Посмотреть сообщение
Я так понял, что ты "долбишь" одно и то-же устройство читая 80 разных регистров.
Не, я долблю одно и то-же устройство читая 9999 регистров в цикле по 125 штук за раз (всего 80 раз выходит). Читал, что если читать по Modbus TCP то должно в несколько десятков миллисекунд чтение проходить. Если по Modbus RTU, то дольше конечно.
0
484 / 397 / 68
Регистрация: 14.02.2014
Сообщений: 1,930
13.06.2017, 17:00
Цитата Сообщение от Chizel Посмотреть сообщение
Читал, что если читать по Modbus TCP
Ну во-первых, пакет по сети может передаваться не моментально (для локальной менее актуально, для интернет - более);
Во-вторых, устройство отвечает тоже не моментально.
Получив запрос изделие должно:
1) Удостовериться, что пакет получен полностью;
2) Пересчитать контрольную сумму;
3) Сверить контрольную сумму из запроса с рассчитанной;
4) Разобрать запрос и понять что требуется;
5) Сформировать ответ;
6) Рассчитать контрольную сумму ответа и добавить её в ответ;
7) Переслать ответ обратно.

При этом надо учитывать, что внутри устройства крайне редко ставятся Intel Core i9 и помимо задачи обмена с пользователем есть куча других, которые (как-правило) имеют больший приоритет, то полторы секунды на опрос 9999 регистров - вполне нормальное время.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
13.06.2017, 17:00
Помогаю со студенческими работами здесь

Разработал класс Money денежного счета, нужна критика и совет с функцией
Доброго времени суток! Очень прошу помоши, прошу бегло взглянуть на мой гавнокод и посоветовать, до чего тут можно доебатся. Мой опыт...

критика по оптимизации
Вот сообственно сайт: http://smart60.kiev.ua Какие есть грубые ошибки в оптимизации? Какие есть советы по конкретному проекту?

Критика по оптимизации
Прошу выдать критику по качеству опитимизации сайта, на счет того что дизайн лажеватый и контента маловато то можете и не говорить и так...

Критика по оптимизации, рекомендации.
Всем, добрый день! Будьте любезны, посмотрите мой сайт estorrent.org. Решил заняться увеличением посещаемости все что знал проработал,...

Критика кода и советы по поводу оптимизации
Всем добрый вечер. Написал игру крестики-нолики в текстовом режиме для 2 игроков. Работает как и должна. Хотел бы увидеть комментарии по...


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

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Новые блоги и статьи
Загрузка PNG-файла с альфа-каналом с помощью библиотеки SDL3_image на Android
8Observer8 27.01.2026
Содержание блога SDL3_image - это библиотека для загрузки и работы с изображениями. Эта пошаговая инструкция покажет, как загрузить и вывести на экран смартфона картинку с альфа-каналом, то есть с. . .
влияние грибов на сукцессию
anaschu 26.01.2026
Бифуркационные изменения массы гриба происходят тогда, когда мы уменьшаем массу компоста в 10 раз, а скорость прироста биомассы уменьшаем в три раза. Скорость прироста биомассы может уменьшаться за. . .
Воспроизведение звукового файла с помощью SDL3_mixer при касании экрана Android
8Observer8 26.01.2026
Содержание блога SDL3_mixer - это библиотека я для воспроизведения аудио. В отличие от инструкции по добавлению текста код по проигрыванию звука уже содержится в шаблоне примера. Нужно только. . .
Установка Android SDK, NDK, JDK, CMake и т.д.
8Observer8 25.01.2026
Содержание блога Перейдите по ссылке: https:/ / developer. android. com/ studio и в самом низу страницы кликните по архиву "commandlinetools-win-xxxxxx_latest. zip" Извлеките архив и вы увидите. . .
Вывод текста со шрифтом TTF на Android с помощью библиотеки SDL3_ttf
8Observer8 25.01.2026
Содержание блога Если у вас не установлены Android SDK, NDK, JDK, и т. д. то сделайте это по следующей инструкции: Установка Android SDK, NDK, JDK, CMake и т. д. Сборка примера Скачайте. . .
Использование SDL3-callbacks вместо функции main() на Android, Desktop и WebAssembly
8Observer8 24.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
моя боль
iceja 24.01.2026
Выложила интерполяцию кубическими сплайнами www. iceja. net REST сервисы временно не работают, только через Web. Написала за 56 рабочих часов этот сайт с нуля. При помощи perplexity. ai PRO , при. . .
Модель сукцессии микоризы
anaschu 24.01.2026
Решили писать научную статью с неким РОманом
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru