8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 338

Почему пропадают/искажаются сообщения SerialPort?

06.03.2025, 17:09. Показов 6571. Ответов 61

Студворк — интернет-сервис помощи студентам
Программа должна обмениваться данными с несколькими платами Arduino(сейчас подключаю 3 шт., планируется больше, но проблемы появляются и ни одной шт., и на двух…).
В тестовом режиме режиме я отправляю “99/0/0/0” – плата считывает это сообщение и отправляет обратно. В мониторе порта программы Arduino все работает четко и стабильно, без каких-либо сбоев (во всяком случае, мне не удалось такое обнаружить).
Но вот когда подключаю написанную программу – начинаются проблемы:
часто вместо отправленного сообщения программа получает не отображаемые символы, в кодировке Asc номера: 13, 10. Иногда программа вообще не получает никакого ответа.
В программе использованы библиотеки:
VB.NET
1
2
3
4
5
6
Imports System.IO.Ports
Imports System.Threading
Imports System.Runtime.InteropServices
Imports System.Windows.Forms.Design
Imports System.IO
Imports System.Text
Программа формирует коллекцию COM-портов:
VB.NET
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
Public oNameCol As New Dictionary(Of [String], [String])() ' массив СОМ-портов с номерами ответов плат
Public oNunbCol As New Dictionary(Of [String], [String])() ' массив номеров плат с СОМ-портами
Public oNPlat As Integer = 0 '  счетчик количества подключенных плат 
Public oComPort As New List(Of SerialPort) ' коллекция СОМ-портов
Public oComSpeed As Integer = 250000 ' скорость сомпорта
Public oReadCom As String ' переменная для чтения ответа из COM-порта
Dim WithEvents mySerialPort As SerialPort
 
    Private Sub ButtR_Click(sender As System.Object, e As System.EventArgs) Handles ButtR.Click
        ReadNameCom()
     End Sub
 
    Private Sub ReadNameCom()
        Dim oPorts As String() = SerialPort.GetPortNames()
        oNPlat = 0
        oNameCol.Clear()
        oNunbCol.Clear()
        If oComPort.Count > 0 Then
            Try
                For i = 0 To oComPort.Count - 1
                    oComPort(i).Close()
                Next
            Catch ex As Exception
            End Try
        End If
        oComPort.Clear()
        For Each oName In oPorts
            If oName <> "COM1" Then
                Try
                    Try
                        Try
                            mySerialPort.Close()
                        Catch ex3 As Exception
                        End Try
                        mySerialPort = New SerialPort(oName)
                        With mySerialPort
                            .BaudRate = oComSpeed '57600 '9600
                            .Parity = Parity.None
                            .StopBits = StopBits.One
                            .DataBits = 8
                            .Handshake = Handshake.None
                            .RtsEnable = True
                            .Open()
                        End With
                    Catch ex As Exception
                    End Try
                    oReadCom = ""
                    mySerialPort.WriteLine("97/0/0/0")
                    Thread.Sleep(90)
                    oReadCom = mySerialPort.ReadExisting()
                    If oReadCom = "" Then
                        Thread.Sleep(50)
                        oReadCom = mySerialPort.ReadExisting()
                    End If
                    mySerialPort.Close()
                Catch ex2 As Exception
                End Try
                If oReadCom <> "" Then
                    oNameCol.Add(oName, oReadCom) 
                    oNunbCol.Add(oReadCom, oName) 
                    oNPlat = oNPlat + 1
                End If
            End If
        Next
        Try
            If oNunbCol.Count > 0 Then
                For i = 1 To oNunbCol.Count
                    If oNunbCol.ContainsKey(i.ToString) Then
                        ListBox1.Items.Add(i.ToString & "  -  " & oNunbCol(i.ToString))
                        oComPort.Add(New SerialPort(oNunbCol(i.ToString)))
                        With oComPort(i - 1) ' нумерация от 0
                            .BaudRate = oComSpeed '57600
                            .Parity = Parity.None
                            .StopBits = StopBits.One
                            .DataBits = 8
                            .Handshake = Handshake.None
                            .RtsEnable = True
                        End With
                    Else
                    End If
                Next
            End If
        Catch ex As Exception
            ButtR.BackColor = Color.Red
        End Try
    End Sub
Провожу тест в цикле:
VB.NET
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
    Private Sub ButStart_Click(sender As System.Object, e As System.EventArgs) Handles ButStart.Click
            For i = 1 To 100
                    SubStart()
            Next
    End Sub
 
    Private Sub SubStart()
        Dim oText As String = ""
        oText = ComsRead() ' ОТПРАВКА ЗАПРОСА
        Debug.Print(oText)
    End Sub
 
    Public oActivText As String ' переменная содержит последний ответ полученный в событии COM-порта
 
    Function ComsRead() As String ' функция отправки и получения ответа из COM-порта
        ComsRead = ""
        Try
            Dim oCallP2 As Integer = 1 ' номер платы чтения сигнала
            Dim oMess1 As String ' тест сообщения в COM-порт подачи сигнала
            oActivText = ""
            AddHandler oComPort(oCallP2).DataReceived, AddressOf DataReceivedHandler ' включение события 
            If oCallP2 = 1 Then
                oMess1 = "99/0/0/0"
                oComPort(oCallP2).WriteLine(oMess1)
            End If
            Dim oText As String = ""
            Thread.Sleep(5)
            For i = 1 To 50
                Thread.Sleep(10)
                oText = oActivText
                If oText <> "" Then
                    i = 999
                End If
            Next i
            RemoveHandler oComPort(oCallP2).DataReceived, AddressOf DataReceivedHandler 
            oComPort(oCallP2).DiscardInBuffer()
            If InStr(oText, "99") = 0 Then
                If oText <> "" Then ' просмотр кодов нештатных ответов
                    TextPin.Text = ""
                    For i = 1 To oText.Length
                        TextPin.Text = TextPin.Text & "  " & Asc(Mid(oText, i, 1))
                        Debug.Print(i.ToString & "=" & Asc(Mid(oText, i, 1)))
                    Next
                End If
            If InStr(oText, "99") = 0 Then
                oText = ""
                oComPort(oCallP2).Close()
                oComPort(oCallP2).Open()
                If oCallP2 = 1 Then
                    oMess1 = "99/0/0/0" 
                    oComPort(oCallP2).WriteLine(oMess1)
                End If
                Thread.Sleep(100) 
                oText = oComPort(oCallP2).ReadExisting()
                If oText = "" Then
                    Thread.Sleep(50) 
                    oText = oComPort(oCallP2).ReadExisting()
                End If
            End If
            ComsRead = oText
        Catch ex As Exception
        End Try
    End Function
 
    Private Sub DataReceivedHandler(sender As Object, e As SerialDataReceivedEventArgs)
        Try
            Dim sp As SerialPort = CType(sender, SerialPort)
            Dim indata As String = sp.ReadExisting()
            oActivText = indata
        Catch ex As Exception
            oActivText = "Error DataReceivedHandler"
        End Try
    End Sub
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
06.03.2025, 17:09
Ответы с готовыми решениями:

Visual studio 2019. Компонент SerialPort, почему не активен, не добавляется?
Добрый день. Установил Visual studio 2019. Пишу программу для работы с последовательным портом. В примерах, ктр видел, люди перетаскивают...

Visual studio 2019. Элемент serialport, почему нет метода getportnames?
В общем установил я вижуал студио 2019 и решил накидать простую программку для работы с серийным портом но оказалось что метод с помощью...

Почему пропадают элементы в конструкторе формы?
Добрый день. Не могу понять почему пропадают элементы в конструкторе формы. Что делал до этого: У меня есть форма с необходимыми...

61
2282 / 1598 / 400
Регистрация: 26.06.2017
Сообщений: 4,726
Записей в блоге: 1
07.03.2025, 09:15
У вас скорее всего это из-за блокировки UI потока.
Например в этом участке кода поток отпускается совсем не надолго:
VB.NET
1
2
3
4
5
6
7
8
Thread.Sleep(5)
            For i = 1 To 50
                Thread.Sleep(10)
                oText = oActivText
                If oText <> "" Then
                    i = 999
                End If
            Next i

Не по теме:

Код настолько (даже не знаю как назвать) линейный что ли, что даже те объекты, вроде СОМ-порта, которые пытаются работать в события, то ломаются об блокировку. filat18, вам реально надо изменить код под паттерн наблюдатель.



Добавлено через 5 минут
Вместо блокировки потока, вроде той, на которую я указал, запускайте таймер ожидания ответа.
Также измените название, а лучше разделите функционал метода ComsRead. Дело в том, что этот метод не только читает, что можно подумать на основании его названия, но и создаёт порты, пишет в них, обрабатывает нештатные ситуации. Короче, метод делает много чего и это много надо декомпозировать.
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 338
07.03.2025, 10:29  [ТС]
А в чем смысл задержек ReadTimeout и WriteTimeout - стоит ли их задавать?
И стоит ли очищать буферы DiscardInBuffer/DiscardOutBuffer перед отправкой-чтением?
0
2282 / 1598 / 400
Регистрация: 26.06.2017
Сообщений: 4,726
Записей в блоге: 1
07.03.2025, 11:52
Я не сильно разбираюсь в СОМ-портах, поэтому не подскажу.
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 338
07.03.2025, 12:06  [ТС]
Цитата Сообщение от Uswer Посмотреть сообщение
Код настолько (даже не знаю как назвать) линейный что ли
Абсолютно верное замечание и я с ним полностью согласен! Предложенный материал по паттерну написан на C#, а я его синтаксис туго понимаю, может кто подкинет пример реализации на VB?
0
Эксперт .NET
 Аватар для Rius
13068 / 7629 / 1669
Регистрация: 25.05.2015
Сообщений: 23,180
Записей в блоге: 14
07.03.2025, 12:25
У вас способ обмена запрос-ответ?
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 338
07.03.2025, 12:36  [ТС]
Цитата Сообщение от Rius Посмотреть сообщение
У вас способ обмена запрос-ответ?
Да.

Я вот хэлп еще раз почитал... Вроде получилось пример кода применить:
VB.NET
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
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim readThread As New Thread(AddressOf Read)
 
        _serialPort = New SerialPort()
 
        _serialPort.PortName = "COM14" ' SetPortName(_serialPort.PortName)
        _serialPort.BaudRate = 250000 ' SetPortBaudRate(_serialPort.BaudRate)
        _serialPort.Parity = Parity.None ' SetPortParity(_serialPort.Parity)
        _serialPort.DataBits = 8 'SetPortDataBits(_serialPort.DataBits)
        _serialPort.StopBits = StopBits.One ' SetPortStopBits(_serialPort.StopBits)
        _serialPort.Handshake = Handshake.None ' SetPortHandshake(_serialPort.Handshake)
 
        _serialPort.Open()
        readThread.Start()
        For i = 1 To 100
            _continue = True
            'Thread.Sleep(5)
            While _continue
                If InStr(oTvet, "99") > 0 Then
                    Debug.Print(i.ToString)
                    _continue = False
                Else
                    oTvet = ""
                    _serialPort.WriteLine("99/0/0/0")
                End If
            End While
        Next i
        _serialPort.DiscardInBuffer()
        _serialPort.DiscardOutBuffer()
        _serialPort.Close()
        TextBox1.Text = TextBox1.Text & "+"
 
        readThread.Join()
        'readThread.Interrupt()
        'readThread.Abort()
    End Sub
 
    Public Shared oTvet As String
 
    Public Shared Sub Read()
        While _continue
            '    If _serialPort.IsOpen = True Then
            Try
                oTvet = _serialPort.ReadLine()
            Catch ex As Exception
            End Try
        End While
    End Sub
Вот только поток закрывается с ошибкой в строке oTvet = _serialPort.ReadLine(). Можно ли его закрыть корректно?
Пробовал добавить условие: If _serialPort.IsOpen = True Then, но без результатно...
0
2282 / 1598 / 400
Регистрация: 26.06.2017
Сообщений: 4,726
Записей в блоге: 1
10.03.2025, 08:27
filat18, у СОМ-порта есть события при получении данных. Вам надо использовать их вместо постоянного чтения.
0
Эксперт .NET
 Аватар для Rius
13068 / 7629 / 1669
Регистрация: 25.05.2015
Сообщений: 23,180
Записей в блоге: 14
10.03.2025, 08:34
Для запрос-ответ не надо. Лучше write и read с правильными таймаутами.
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 338
10.03.2025, 09:48  [ТС]
Цитата Сообщение от Uswer Посмотреть сообщение
у СОМ-порта есть события при получении данных
Да я знаю и не против бы, но как Вы выше правильно отметили: прямолинейность кода не позволяет ему планомерно (без искажений и потерь) выдавать ответы в основной поток.
Цитата Сообщение от Rius Посмотреть сообщение
write и read с правильными таймаутами.
Очень интересное замечание! Как определить какие таймауты правильные?
0
Эксперт .NET
 Аватар для Rius
13068 / 7629 / 1669
Регистрация: 25.05.2015
Сообщений: 23,180
Записей в блоге: 14
10.03.2025, 09:52
Как определить какие таймауты правильные?
Есть время ожидания ответа, пока прибор думает, что ответить. Между отправкой запроса и первым байтом ответа. Указывается в документации, смотрится на осциллографе или логическом анализаторе, находится опытным путём.
И есть время таймаута посылки. Например, равное времени передачи 3-5 байт на используемой скорости. Если оно истекло, то пакет завершён.
0
Эксперт .NET
 Аватар для Rius
13068 / 7629 / 1669
Регистрация: 25.05.2015
Сообщений: 23,180
Записей в блоге: 14
10.03.2025, 16:24
Code
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
массив_байт отправить_и_принять(массив_байт):
    очищаем буферы чтения и записи порта
    пишем отправляемый массив в порт
 
    ставим таймаут чтения 1 секунду
    читаем один байт
 
    если вылетело исключение по таймауту:
        возвращаем пустой массив
 
    добавляем считанный байт в массив результата
 
    ставим таймаут чтения равный времени передачи 3 байт на используемой скорости
 
  повтор чтения:
    читаем 4096 байт из порта в буфер
    добавляем в массив результата столько, сколько считалось
 
    если считалось 0:
        возвращаем массив результата
 
    если вылетело исключение по таймауту:
        возвращаем массив результата
 
    перейти к повтору чтения
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 338
08.04.2025, 08:19  [ТС]
Цитата Сообщение от Uswer Посмотреть сообщение
вам реально надо изменить код под паттерн наблюдатель.
Вот я все же, как мне кажется, нашел пример реализации предложения в VB.
Но, увы пока есть только ощущение наличия рационального зерна... Перманентный цейтнот и подкрадывающийся писец, не дает осознать суть наблюдателя... Может кто-нибудь в двух словах набросать структуру переадресаций и пояснить: где ведущий/ведомый...?
0
2282 / 1598 / 400
Регистрация: 26.06.2017
Сообщений: 4,726
Записей в блоге: 1
08.04.2025, 11:44
Шаблон Наблюдатель широко распространён в формах в виде механизма событий. Суть до безобразия проста: один объект, например СОМ-порт, имеет (определяет) события, другой объект, например экземпляр формы, подписывается (регистрирует свои методы) на события первого. Что происходит в кулуарах? Там следующее на нашем примере - отправитель события (СОМ-порт) при обнаружении данных на входе записывает их в буфер обмена, после чего возбуждает соответствующее событие, при этом все получатели события обрабатывают данные в буфере как им хочется. Как происходит возбуждение события? На самом деле при подписывании метода на событие происходит запись делегата в список делегатов для данного события. Когда отправителю надо оповестить подписчиков о чём-то, то он из соответствующего типу события списка поочерёдно вызывает делегатов. Вот и всё.
0
Модератор
1245 / 676 / 292
Регистрация: 10.11.2019
Сообщений: 1,406
09.04.2025, 17:16
Есть простой и надёжный способ чтения данных из COM порта, который почему-то не описан
в рекомендациях Microsoft. Создаём таймер с периодом скажем 100 миллисекунд. Для этого таймера пишем:

VB.NET
1
2
3
4
5
6
7
8
9
10
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Dim r As Integer, i As Integer
        If Not SerialPort1.IsOpen Then Exit Sub
        While SerialPort1.BytesToRead <> 0
            r = SerialPort1.ReadByte
'
' здесь идёт обработка принятых байт, типа записи их в массив чтения, установка флагов готовности данных  и т д
'
        End While
    End Sub
Таким образом все приёмные байты гарантированно ловятся. Даже если предположить
высокую скорость обмена, вроде 115 КБод, число принятых байт (за 100 миллисекунд таймера)
не превысит 1000-2000, что быстро отрабатывается такой SUB
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 338
16.04.2025, 07:33  [ТС]
Цитата Сообщение от qbfan Посмотреть сообщение
.ReadByte
А в чем преимущества использования .ReadByte, по сравнению с .ReadExisting?
0
Модератор
1245 / 676 / 292
Регистрация: 10.11.2019
Сообщений: 1,406
16.04.2025, 16:04
Не понравилось мне описание ReadExisting

Цитата из MSDN:

Считывает все непосредственно доступные байты в соответствии с кодировкой из потока
и из входного буфера объекта SerialPort.


"в соответствии с кодировкой" - это как понимать ?

И далее:

Примечание
Поскольку класс SerialPort предусматривает буферизацию данных, в отличие от потока,
задаваемого свойством BaseStream, между ними возможен конфликт при определении
числа байтов, доступных для чтения. Свойство BytesToRead может показывать, что байты
для чтения имеются, однако они могут оказаться недоступными для потока,
определенного свойством BaseStream, поскольку помещены в буфер для SerialPort.


В общем .ReadByte представляется более надёжным и не даёт повода для ненужных ошибок.
К тому же среди программистов есть два подхода к обмену через последовательный порт.
Кто-то считает (вполне разумно), что можно передавать через него любые бинарные данные,
а правильность передачи можно контролировать контрольными суммами.
Второй подход - это передача всех данных (в том числе чисел) в текстовом формате,
произвольный двоичный код можно при этом передавать как HEX цифры. Это вроде бы медленней,
но позволяет отлаживать выходные данные микроконтроллера с помощью программы-терминала,
а не писать с нуля свою программу, которая способна принять и напечатать например байт 00h
0
Администратор
Эксперт .NET
 Аватар для OwenGlendower
18250 / 14174 / 5366
Регистрация: 17.03.2014
Сообщений: 28,850
Записей в блоге: 1
18.04.2025, 08:40
Цитата Сообщение от qbfan Посмотреть сообщение
"в соответствии с кодировкой" - это как понимать ?
У класса SerialPort есть свойствоEncoding. Оно и задает кодировку.
0
Модератор
1245 / 676 / 292
Регистрация: 10.11.2019
Сообщений: 1,406
18.04.2025, 09:45
Цитата Сообщение от OwenGlendower Посмотреть сообщение
У класса SerialPort есть свойствоEncoding. Оно и задает кодировку.
Я ответил на вопрос filat18 в том смысле, что .ReadExisting возвращает текстовую строку,
да ещё вероятно не в 8-битной кодировке, так что использовать .ReadExisting для приёма двоичных данных
не стоит.
0
Администратор
Эксперт .NET
 Аватар для OwenGlendower
18250 / 14174 / 5366
Регистрация: 17.03.2014
Сообщений: 28,850
Записей в блоге: 1
18.04.2025, 13:42
Цитата Сообщение от qbfan Посмотреть сообщение
использовать .ReadExisting для приёма двоичных данных не стоит.
Однозначно.

Цитата Сообщение от qbfan Посмотреть сообщение
Я ответил на вопрос filat18
А я ответил на ваш вопрос про кодировку
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
18.04.2025, 13:42
Помогаю со студенческими работами здесь

Почему пропадают созданные ячейки таблицы?
Проблемка в удалении данных из контролла (к примеру из таблицы) при обновлении страницы (например по нажатии кнопки). Подскажите как...

Serialport.write и serialport.basestream.write - в чем разница
в чем разница между следующими способами отправки данных на ком-порт?: serPort.BaseStream.Write(data,0,datalen); ...

C# SerialPort Неполный прием сообщений
Добрый день. Долго искал решение своей проблемы, но поиски оказались тщетны. Задача: организовать обмен сообщениями между железом и...

Почему не приходит сообщение группе SignalR?
Методы из хаба. public void UserConnect(string message) { Clients.Group(&quot;managers&quot;).notifyManager(Context.ConnectionId, message);...

Почему в отправляемом почтовом сообщении появляются вопросики?
Все русские символы в отправляемом почтовом сообщении почему-то заменены на вопросики? Может надо указывать кодировку письма? Если да, то...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

Новые блоги и статьи
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
Модель микоризы: классовый агентный подход 2
anaschu 06.01.2026
репозиторий https:/ / github. com/ shumilovas/ fungi ветка по-частям. коммит Create переделка под биомассу. txt вход sc, но sm считается внутри мицелия. кстати, обьем тоже должен там считаться. . . .
Расчёт токов в цепи постоянного тока
igorrr37 05.01.2026
/ * Дана цепь постоянного тока с сопротивлениями и напряжениями. Надо найти токи в ветвях. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа и решает её. Последовательность действий:. . .
Новый CodeBlocs. Версия 25.03
palva 04.01.2026
Оказывается, недавно вышла новая версия CodeBlocks за номером 25. 03. Когда-то давно я возился с только что вышедшей тогда версией 20. 03. С тех пор я давно снёс всё с компьютера и забыл. Теперь. . .
Модель микоризы: классовый агентный подход
anaschu 02.01.2026
Раньше это было два гриба и бактерия. Теперь три гриба, растение. И на уровне агентов добавится между грибами или бактериями взаимодействий. До того я пробовал подход через многомерные массивы,. . .
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост.
Programma_Boinc 28.12.2025
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост. Налог на собак: https:/ / **********/ gallery/ V06K53e Финансовый отчет в Excel: https:/ / **********/ gallery/ bKBkQFf Пост отсюда. . .
Кто-нибудь знает, где можно бесплатно получить настольный компьютер или ноутбук? США.
Programma_Boinc 26.12.2025
Нашел на реддите интересную статью под названием Anyone know where to get a free Desktop or Laptop? Ниже её машинный перевод. После долгих разбирательств я наконец-то вернула себе. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru