Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 16.09.2021, 23:54   #1
Foxpronet
Пользователь
 
Регистрация: 11.08.2011
Сообщений: 67
По умолчанию COM-интерфейс. Отправка команды и получение ответа

Здравствуйте. Заранее прошу прощение за длинное вступление с исходными данными, но дьявол, как известно кроется в деталях..( Есть устройство подключенное через COM-порт RS-232С, нужно посредством своей программы передать и получить ответ только для одной команды (вообще их там много, но смогу понять принцип и подтянуть другие уже самостоятельно).

Есть спецификация (ниже), из минусов: знаний- 0, всегда сторонился COM и иже с ним.. Также нет возможности опробовать код "в деле". Но показалось, что шаги довольно чётко расписаны в доках, - посему понадеялся на лучшее (в след.комментарии, то что наваял в Delphi). Вроде бы все норм., но есть сомнения..

Посмотрите пожалуйста профессиональным взлядом, все-ли правильно в коде (с точки зрения синтаксиса, т.е понятное дело, - все компилируется в Delphi 10.1), а то реально нет пока возможности затестить все это хозяйство в паре с купюрораздатчиком.. Прикрепил на всякий случай исходники и оригинальный PDF-файл спецификации, откуда почерпнул вводную информацию ниже по тексту

Вводные данные

Общ. схема взаимодействия Клиент-Сервера:
  1. >>> Запрос. ПК -> Диспенсер
  2. <<< ACK (байт-значение 0x06) - флаг, что исх.Запрос была распознана и принята устройством (диспенсером))
  3. <<< Ответ.
  4. >>> Тот же - ACK посыл. ПК -> Диспенсер
  5. <<< EOT (флаг,используется в заголовке и конце сообщения)

Характеристики:

Метод передачи - полудуплексный режим (HDM). Когда диспенсер работает, сообщение с верхнего уровня (ПК) игнорируется. Основные передаваемые символы приведены ниже.

Код:
  • Transmission Rate - 9600 bps
  • Character Length - 8 bits
  • Parity bits - Even
  • Stop bits - 1 stop bit
  • Flow Control - None
В случае передачи физическое "рукопожатие" не используется.
Соблюдаются только RXD и TXD спецификация, определеные в RS-232C
Оригинальный текст:
In case of transmission, physical handshake is not used. Only RXD
and TXD defined in RS-232C specification is observed.

Основные тайминги (Минимум - Макс.):

Код:
Delay to send ACK after Command:        0 -  50 ms 
Delay to send EOT after ACK:            0  - 50 ms 
Timeout for waiting for ACK:            5000 ms - 5050 ms 
Delay to send Response after Command:   0 - 90 sec

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

Код:
Запрос
//--------------------//
Name     Code  Description 
EOT      0x04  Start of Transmission 
ID       0x30  Communication ID 
STX      0x02  Start of Text  
CMD            Command Code 
PARA           Command PARAmeter (Variable Length) 
ETX      0x03  End of Text 
BCC            Block Check Character 
//--------------------//
Код:
Ответ
//--------------------//
Name    Code  Description 
SOH     0x01  Start of Header
ID      0x30  Communications ID
STX     0x02  Start of Text
RSP           Command Code
ERROR         Error Code + 0x20
PARA          Response PARAmeter (Variable Length)
ETX     0x03  End of Text
BCC           Block Check Character
//--------------------//
Вот с этим "беда" на вскидку, не могу сообразить..
Код:
//////////////////////////
// BCC can be gotten through Exclusive-OR (XOR) from the start of each message to ETX except BCC.
// BCC можно получить с помощью Exclusive-OR (XOR) от начала каждого сообщения до ETX, кроме BCC.  
//////////////////////////
Далее сама команда, которую "собрал" в коде:
Описание из документации (постарался привести к читабельному виду):

Цитата:
Команда приведет к выдаче запрошенного количества банкнот из выбранной кассеты.
Диспенсер проверит толщину и длину банкнот, которые соотносятся с
OPACITY и LENGTH, а затем решит, выдать ли банкноты или отклонить.
Во время процесса другие параметры, такие как необходимое расстояние между купюрами
и перекос самих банкнот также влияют на выдачу и отклонение.

Количество запрошенных банкнот для выдачи не должно превышать 20.

Поле SERIAL предназначено для последовательного подсчета и играет роль идентификации команды дозатора.
Если текущий SERIAL такой же, как и у предыдущей команды, произойдет ошибка(0x3D).
Чтобы избежать неожиданной путаницы в команде «Раздать», хост должен посылать разные
числа или последовательные номера каждый раз на SERIAL, когда он отправляет команду Disapnese в VCDM.
Команда "DISPENSE" (Multi-Cassette Dispense)

Код:
Запрос:
//--------------------------------------//
Name     Code          Description 
EOT      0x04          Start of Transmission  
ID       0x30          Communication ID 
STX      0x02          Start of Text 
CMD      0x52          DISPENSE Command 
QTY1     0x20~         The number of bills to be dispensed from Top Cassette + 0x20 
QTY2     0x20~         The number of bills to be dispensed from the Second Top Cassette + 0x20 
QTY3     0x20~         The number of bills to be dispensed from the Third Top Cassette + 0x20 
QTY4     0x20~         The number of bills to be dispensed from Bottom Cassette + 0x20 
TO1      0x20          Default Status: Fixed as 0x20 
TO2      0x20          Default Status: Fixed as 0x20 
SERIAL   0x21~ 0x7F    Dispense  Serial  Number  or  Identifiaction  Number  of Dispense Command 
ETX      0x03          End of Text  
BCC                    Block Check Character 
//--------------------------------------//
Код:
Ответ:
//--------------------------------------//
Name     Code          Description 

SOH      0x01          Start of Header  
ID       0x30          Communication ID 
STX      0x02          Start of Text 
RSP      0x52          DISPENSE Command 
ERROR                  Error Status for Operation 
SERIAL   0x21~ 0x7F    Dispense  Serial  Number  or  Identifiaction  Number  of Dispense Command 

EXIT1    Count +0x20   Number of Items Dispensed from the Top Cassette. 
REJECT1  Count +0x20   Number of Reject Events from the Top Cassette 
CASSETTE1 0x31~0x34    The Type of the Cash Cassette Loaded on the 1st High (Reserved.) Default is 0x31 

EXIT2     Count+0x20   Number of Items Dispensed from the Second Top Cassette. 
REJECT2   Count+0x20   Number of Reject Events from the Second Top Cassette 
CASSETTE2 0x31~0x34    The Type of the Cash Cassette Loaded on the 2nd  High (Reserved.) Default is 0x32 

EXIT3     Count+0x20   Number of Items Dispensed from the Third Top Cassette. 
REJECT3   Count+0x20   Number of Reject Events from the Third Top Cassette 
CASSETTE3 0x31~0x34    The Type of the Cash Cassette Loaded on the 3rd  High (Reserved.) Default is 0x33 

EXIT4     Count+0x20   Number of Items Dispensed from the Bottom Cassette. 
REJECT4   Count+0x20   Number of Reject Events from the Bottom Cassette. 
CASSETTE4 0x31~0x34    The Type of the Cash Cassette Loaded on the 4th  High (Reserved.) Default is 0x34 

RSV       0x20         Reserved (9bytes) 
ETX       0x03         End of Text  
BCC                    Block Check Character 
//--------------------------------------//
Вложения
Тип файла: pdf PL-VCDM0000-003 VCDM Interface Specification 2.7.pdf (892.0 Кб, 6 просмотров)
Тип файла: zip Project. Delphi 10.1 Berlin.zip (58.5 Кб, 10 просмотров)

Последний раз редактировалось Foxpronet; 17.09.2021 в 04:06. Причина: ЧелФактор
Foxpronet вне форума Ответить с цитированием
Старый 16.09.2021, 23:55   #2
Foxpronet
Пользователь
 
Регистрация: 11.08.2011
Сообщений: 67
По умолчанию

/////////////////////////////////////
//Сам код
////////////////

Код:
// главная и ед. кнопка инициализации раб.процесса
procedure Tf_.sb1Click(Sender: TObject);
var
  s: string; i: integer;
begin
 // получаем строку КОМ-порта из поля ввода
 s_com:= 'COM'+ec.Text;

 // не удалось открыть порт, - лог и выход
 if not openCom(s_com, 2048, 2048, 9600) then
 begin
   f_.lg.lines.add('Error in "opencom(..)"');
   EXIT
 end;

 // Запрос
 {формируем строку запроса исходя из документации в 1-ом комментарии}

 s:= #4+  // EOT - фикс.значение - всегда 0x04 (Start of Transmission)
     #48+ // ID  - фикс.значение - всегда 0x30 (Communication ID)
     #2+  // STX - фикс.значение - всегда 0x02 (Start of Text)
     #82+ // CMD - фикс.значение - всегда 0x52 (DISPENSE Command) - основная комманда

     #33+ // QTY1 - фикс.значение - 0x20 + кол-во купюр, которое хотим получить из кассеты 1 (для примера выставил 1 банкноту = #32 + #1)
     #34+ // QTY2 - фикс.значение - 0x20 + кол-во купюр, которое хотим получить из кассеты 2 (для прим. - 2 купюры   = #32 + #2)
     #35+ // QTY3 - фикс.значение - 0x20 + кол-во купюр, которое хотим получить из кассеты 3 (для прим. - 3 купюры   = #32 + #3)
     #36+ // QTY4 - фикс.значение - 0x20 + кол-во купюр, которое хотим получить из кассеты 4 (для прим. - 4 купюры   = #32 + #4)

     #32+ // TO1 - фикс.значение - 0x20 - "Default Status: Fixed as 0x20" - т.е фиксировано
     #32+ // TO2 - фикс.значение - 0x20 - "Default Status: Fixed as 0x20" - т.е фиксировано

     {get_nnChar просто увеличивает каждый раз на 1 предыдущее число, т.е каждом ЛКМ по кнопке будет 1,2,3 ... итд }
     f_.get_nnChar+   //SERIAL: 0x21 ~ 0x7F (#33 - #127) <<< цифра в запросе НЕ должна содержать предыдущее значение, так понял, - вроде проверки безопасности транзакции, типа уник.номер в заданном интервале (от 33 до 127) ?

     #3+  // ETX - фикс.значение - 0x03 - "End of Text"

     f_.get_BCC;  // Пока не понял как получить - "Block Check Character"   :( В ответе вроде-как его можно игнорировать, но для запроса он, понятное дело, важен.


 // чистим лог..
 lg.Clear;
 lg.lines.add('Отправляем команду на порт: '+s_com); lg.lines.add('['+s+']');

 // посылаем сформированную выше строку S
 if not sendcom( s ) then begin lg.lines.add('Error in "sendcom(..)=False"'); EXIT end;
 lg.lines.add('Ждем "флаг одобрения" - ACK в ответе..'); // ACK (0x06): to indicate that message has been accepted. -  "чтобы указать, что сообщение было принято."
                                                    // .. или дожидаемся NAK (0x15) - что все плохо ( "to indicate that the message has been rejected and that the message should be resent.")

 is_ok:= False;  // вспомогат. переменная в таймере
 glob_var20;    // тут ициализируются глоб. переменные EXIT,REJECT, приводим к нулю
 tr.Enabled:= True;

end;
Код:
function SetCommTiming: Boolean;
var Timeouts: TCommTimeOuts;
begin
 with TimeOuts do begin
  { Понял, что это тайминги (отметил их в комментарии выше), не знаю нужно-ли их вообще трогать здесь, подскажите пожалуйста}

  ReadIntervalTimeout         := 1;
  ReadTotalTimeoutMultiplier  := 0;
  ReadTotalTimeoutConstant    := 1;
  WriteTotalTimeoutMultiplier := 2;
  WriteTotalTimeoutConstant   := 2;

 end;
 Result:= SetCommTimeouts(F,Timeouts);

 // запись в лог (мемо) на ошибке
 //if not result then f_.lg.lines.add('Error in "SetCommTimeouts"');
end;
Код:
function received_okey(var s: string; var error_flag:byte): boolean;  // Response = Error Code + 0x20
var
  b: array[0..20] of byte; //буфер для чтения данных
  ByteReaded: cardinal; //количество считанных байт
  Status: DWord; //статус устройства
  i: integer;

begin {QueryPort}
  result:= False; error_flag:= ord(0);
  s:= '';

  //получим статус COM-порта устройства
  if not GetCommModemStatus(f, Status) then
  begin {ошибка при получении статуса устройства}
    //ошибка, все выключаем и выходим SysErrorMessage(GetLastError);
    f_.lg.lines.add('Error in "GetCommModemStatus" - ошибка при получении статуса устройства');
    EXIT;
  end; {ошибка при получении статуса устройства}

  //читаем буфер из Com-порта
  FillChar(b, SizeOf(b), #0);
  if not ReadFile(f, b, SizeOf(b), ByteReaded, nil) then
  begin {ошибка при чтении данных}
    //ошибка, все закрываем и уходим SysErrorMessage(GetLastError);
    f_.lg.lines.add('Error in "ReadFile" - ошибка при чтении данных');
    EXIT;
  end; {ошибка при чтении данных}

  //данные пришли
  // наш "клиент", если длина - 21, и есть фикс. флаги 1,48,2,82 в начале
  if ByteReaded = 21 then {>0}
  if (ord(b[0]) = 1) and (ord(b[1]) = 48) and (ord(b[2]) = 2) and (ord(b[3]) = 82) then

  begin
    result:= True;
    // 5-й байт указывает на ошибку на устройстве(диспенсере), если он > 32
    if ord(b[4]) > 32 then
    begin
      error_flag:= b[4];
    end;

    // Присваеваем зн. глоб. переменным
    // выданное число купюр для всех кассет
    EXIT1:= ord(b[6])  -32;
    EXIT2:= ord(b[9])  -32;
    EXIT3:= ord(b[12]) -32;
    EXIT4:= ord(b[15]) -32;

    // забраковано (отправлено купюр в лоток для брака видимо.. хотя черть его знает)
    REJECT1:= ord(b[7])  -32;
    REJECT2:= ord(b[10]) -32;
    REJECT3:= ord(b[13]) -32;
    REJECT4:= ord(b[16]) -32;


    // формируем полную строка ответа для лога
    for i := 1 to Length(b) do  begin
        s := s + ' ' + inttostr(Ord(b[i-1]));
    end;

  end;
end; {QueryPort}
Код:
// процедура таймера. Интервал - 1 мс. Ожидание на получение ответа от устройства
procedure Tf_.trTimer(Sender: TObject);
var ans:char; s: string; error_flag:byte;
begin
  // Откл. на всякий ...
  tr.Enabled:= False; //error_flag:= ord(0);

  // is_ok изначально равно False. Фрагмент кода будет запущен после присвоения True
  if is_ok then
  begin
       // осн.функция на получение и проверку корректности пакета, переданного от сервера, см. выше по коду
       if received_okey(s, error_flag) then
       begin
         // логи:
         lg.lines.add('Команда от сервера принята.');
         lg.lines.add('['+s+']');
         // в error_flag из функции received_okey() сядет байт > 32 если сервер вернет ошибку на самом диспенсере, т.е служебные сообщения об ошибках начинаются с 32
         // купюры там застряли.. или еще что
         if ord( error_flag ) > 32 then
         lg.lines.add('Был возвращен флаг ошибки диспенсера:  #'+ord(error_flag).ToString+' - '+IntToHex(ord(error_flag),4));

         // значения из EXIT1-EXIT4, и REJECT1-REJECT4 получаем также из received_okey() - глобальные переменные и пишем в лог
         lg.lines.add('Выдано купюр (1-4 кассеты): '+EXIT1.ToString+','+EXIT2.ToString+','+EXIT3.ToString+','+EXIT4.ToString);
         lg.lines.add('Забраковано  (1-4 кассеты): '+REJECT1.ToString+','+REJECT2.ToString+','+REJECT3.ToString+','+REJECT4.ToString);

         // завершаем таймер
         EXIT
       end;
       // продолжаем ..
       tr.Enabled:= True; EXIT
  end;

  {readACK вернет chr(Temp) от ReadFile(F, Temp, 1, 1, nil)}
  ans:= readACK;

  // ждем флаг ACK (0x06) от сервера (принял и распознал отправленный нами ранее пакет)
  if ans  = #6 then
  begin
    // получен флаг ACK
       is_ok:= True;
       lg.lines.add('Команда принята сервером. Ждите ответ ..');

  end else
  if ans  = #21 then // NAK (0x15)
  begin
    // получен флаг NAK, не распознал .......
       lg.lines.add('Команда отклонена сервером.');
       EXIT
  end else
  begin
    // получен др. ответ
       lg.lines.add('Получен нераспознанный байт: '+ans);
       EXIT
  end;

  // продолжаем
  tr.Enabled:= True;

end;

Последний раз редактировалось Foxpronet; 17.09.2021 в 04:08.
Foxpronet вне форума Ответить с цитированием
Старый 16.09.2021, 23:56   #3
Foxpronet
Пользователь
 
Регистрация: 11.08.2011
Сообщений: 67
По умолчанию

Код:
function SetCommBuffer(InQueue, OutQueue: LongInt): Boolean;
begin
 {Поставил от-балды InQueue, OutQueue в 2048, не уверен, что правильно....}
 Result:= SetupComm(F, InQueue, OutQueue);

 // запись в лог (мемо) на ошибке
 //if not result then f_.lg.lines.add('Error in "SetCommBuffer"');
end;
Код:
function SetCommStatus(Baud: Integer): Boolean;
var DCB: TDCB;
begin
 with DCB do begin


  DCBlength:=SizeOf(Tdcb);
  {4 параметра ниже есть в спецификации, но не понятно, как можно понимать "Even", это 0 или нечто иное ....}
  BaudRate := Baud;   // <<< "9600 bps";
  ByteSize  :=8;     // <<< "8 bits"
  Parity    :=0;    // <<< "Even" ???
  StopBits  :=1;   // <<< "1 stop bit"

  /// Еще в спецификации есть последняя строчка - "Flow Control = None" - Есть-ли аналог для этого у закоментированных ниже полей ?

  {
  Flags     :=12305;
  wReserved :=0;
  XonLim    :=600;
  XoffLim   :=150;
  XonChar   :=#17;
  XoffChar  :=#19;
  ErrorChar :=#0;
  EofChar   :=#0;
  EvtChar   :=#0;
  wReserved1:=65;
  }
 end;
 Result:= SetCommState(F, DCB);

 // запись в лог:
 //if not result then f_.lg.lines.add('Error in "SetCommStatus"');
end;
Код:
// осн. функция открытия порта
function opencom( comPort:string; InQueue, OutQueue, Baud: LongInt ): Boolean;
begin
 comport:= uppercase(comport);
 if f > 0 then CloseHandle(f);

 F:= CreateFile(PChar(comPort), GENERIC_READ or GENERIC_WRITE,
                        0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if f = 0 then  f_.lg.lines.add('Error in "F = 0"');

 Result:= (F > 0) and SetCommTiming and                       // 3 стандартные под-функции, реализация выше по коду
                      SetCommBuffer(InQueue,OutQueue) and
                      SetCommStatus(Baud)
end;
Код:
// основная функция передачи данных
function sendcom(s: string): boolean;

var TempArray: array[1..255] of Byte;
    Count    : Integer;
    TX_Count : cardinal;
begin
 result:= False;
 for Count:= 1 to Length(S) do TempArray[Count]:= Ord(S[Count]);

 result:= WriteFile(F, TempArray,Length(S),TX_Count,nil)
end;
Код:
// основная функция чтения поступишних данных
function readcom: string;

var RX_Count : cardinal;
    TempArray: array[1..255] of Byte;
    Count    : Integer;
begin
 result:= '';
                  ReadFile(F, TempArray, 255, RX_Count, nil);

 for Count:= 1 to RX_Count do result:= result + Chr(TempArray[Count])
end;

Последний раз редактировалось Foxpronet; 17.09.2021 в 04:09.
Foxpronet вне форума Ответить с цитированием
Старый 17.09.2021, 06:25   #4
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

Тут ошибка
Цитата:
Сообщение от Foxpronet Посмотреть сообщение
StopBits :=1; // <<< "1 stop bit"
BYTE StopBits; // 0,1,2 = 1, 1.5, 2
Цитата:
Parity :=0; // <<< "Even" ???
BYTE Parity; // 0-4=no,odd,even,mark,space
В дельфи есть хорошая справка. Во вторых не используйте маические числа есть же константы для всего.

ONESTOPBIT, EVENPARITY

bcc присваеваешь результату 0 и далее ксоришь все байты сообщения с результатом в цикле.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .
Pavia вне форума Ответить с цитированием
Старый 17.09.2021, 16:40   #5
Foxpronet
Пользователь
 
Регистрация: 11.08.2011
Сообщений: 67
По умолчанию

Понял по всем позициям, спасибо большое! Как думаете, тот ReadFile() в таймере,
не будет-ли приводить к зависанию (синхронный режим кажись), если да, то как можно это устранить (новый флаг какой в той же функции, или другую модель для чтения искать)
Foxpronet вне форума Ответить с цитированием
Старый 17.09.2021, 16:51   #6
Foxpronet
Пользователь
 
Регистрация: 11.08.2011
Сообщений: 67
По умолчанию

И аргументы InQueue, OutQueue в 2048 для openCom() не аукнутся в моем случае (исх. из исходных критериев) ?


Код:
 // не удалось открыть порт, - лог и выход
 if not openCom(s_com, 2048, 2048, 9600) then
 begin
   f_.lg.lines.add('Error in "opencom(..)"');
   EXIT
 end;
Код:
Transmission Rate - 9600 bps
Character Length - 8 bits 
Parity bits           - Even 
Stop bits            - 1 stop bit 
Flow Control        - None
И вот это "Flow Control = None" - т.е видимо, тоже должен быть
отд.флаг для DCB (в функции SetCommStatus), если не ошибаюсь

Последний раз редактировалось Foxpronet; 17.09.2021 в 17:01.
Foxpronet вне форума Ответить с цитированием
Старый 18.09.2021, 11:59   #7
stalkernet
Пользователь
 
Регистрация: 28.02.2009
Сообщений: 42
По умолчанию

Для начала взять схему соединений и выяснить какой кабель нужен.
ибо In case of transmission, physical handshake is not used. Only RXD
and TXD defined in RS-232C specification is observed.
очень уж лаконично сказано.

квбели
1 прямой - 9 проводов.
2 нульмодем 3 провода. 2 перемычки(4-6)(7-8)
3 нульмодем - 5 проводов.
Сделать кабель.

Одна переменная из уравнения

X + Y + Z = Б...ь какого PSже оно не работает... удалена.

теперь берем VirtualComport. ставим не машину и соеденяем два кома как необходимо по схеме.
на один вешаем терминал или эмулятор своего прибора.
на второй - свою программу.
пишем, пробуем, курим RTFM - пока не заработает.

подключаем прибор - начинаем танцы с бубном до получения необходимого результата.

из советов.

если не нужна банковская аттестация софта - забей на WINAPI. возьми готовый компонент типа
BCPort или ComPort Library от Dejan Crnila. Сразу решаешь кучу вопросов.
Если нужна - посмотришь как реализовано - вместо изобретения велосипеда.

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

Ну вобщем както так.

Если нужен софт скажи пришлю на почту.

PS ошибки новичков: 50% - неправильный кабель, 10% - непонимание настроек порта.
ну и не забывать - программа работает как написана, а не как думает программист.
stalkernet вне форума Ответить с цитированием
Старый 18.09.2021, 12:06   #8
stalkernet
Пользователь
 
Регистрация: 28.02.2009
Сообщений: 42
По умолчанию

Хорошая справка по Com порту.

https://cxem.net/comp/comp47.php
stalkernet вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
RCON отправка команды и получение ответа. VarlonecM Работа с сетью в Delphi 0 15.03.2019 11:30
Отправка POST запроса и получение ответа Chester751 Общие вопросы по Java, Java SE, Kotlin 2 19.07.2016 11:45
Получение ответа сервера cyberdev Работа с сетью в Delphi 6 04.04.2016 15:57
Получение ответа сервера. Semreg Общие вопросы Delphi 1 01.12.2013 22:37
О получение ответа AJAX Rost93 JavaScript, Ajax 11 16.05.2012 07:25