Гость
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Работа с COM. Отправка одной команды (пакета) и получение ответа / 24 сообщений из 24, страница 1 из 1
17.09.2021, 03:45
    #40098177
скелА
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
Здравствуйте. Заранее прошу прощение за длинное вступление с исходными данными, но дьявол, как известно кроется в деталях..(
Есть устройство подключенное через COM-порт RS-232С, нужно посредством своей программы передать и получить ответ только
для одной команды (вообще их там много, но смогу понять принцип и подтянуть другие уже самостоятельно).

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

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

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

Общ. схема взаимодействия Клиент-Сервера:

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

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

Код: sql
1.
2.
3.
4.
5.
6.
7.
[LIST]
[*]Transmission Rate - 9600 bps
[*]Character Length - 8 bits 
[*]Parity bits           - Even 
[*]Stop bits            - 1 stop bit 
[*]Flow Control        - None
[/LIST]



В случае передачи физическое "рукопожатие" не используется. Соблюдаются только RXD и TXD спецификация, определеные в RS-232C
Оригинальный текст:
In case of transmission, physical handshake is not used. Only RXD and TXD defined in RS-232C specification is observed.

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

Код: sql
1.
2.
3.
4.
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 




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

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
Запрос
//--------------------//
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 
//--------------------//



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
Ответ
//--------------------//
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
//--------------------//



Вот с этим "беда" на вскидку, не могу сообразить..
Код: sql
1.
2.
3.
4.
//////////////////////////
// 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)

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
Запрос:
//--------------------------------------//
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 
//--------------------------------------//



Код: sql
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.
Ответ:
//--------------------------------------//
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 
//--------------------------------------//



//////////////////////////////////////////////////////////////////////////
//Сам код, что удалось пока интуитивно "склеить"
////////////////

Код: sql
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.
// главная и ед. кнопка инициализации раб.процесса
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;



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
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;



Код: sql
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.
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}



Код: sql
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.
// процедура таймера. Интервал - 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;



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
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;



Код: sql
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.
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;



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
// осн. функция открытия порта
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;



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
// основная функция передачи данных
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;



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
// основная функция чтения поступишних данных
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;
...
Рейтинг: 0 / 0
17.09.2021, 07:34
    #40098183
DmSer
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
Оборудование должно быть под рукой, иначе будете несколько лет отлаживать работу с диспенсером.
...
Рейтинг: 0 / 0
17.09.2021, 08:27
    #40098187
Соколинский Борис
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
скелА
Вот с этим "беда" на вскидку, не могу сообразить..
Код: sql
1.
2.
3.
4.
//////////////////////////
// BCC can be gotten through Exclusive-OR (XOR) from the start of each message to ETX except BCC.
// BCC можно получить с помощью Exclusive-OR (XOR) от начала каждого сообщения до ETX, кроме BCC.  
//////////////////////////

Все просто.
Это контрольный байт, полученный XOR-ом всех предыдущих
Т.е. пишешь что-то вроде
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
function GetBCC(const Buffer; Length: Integer): byte;
 var
  Pdata: PByteArray absolute buffer;
  i: Integer;
begin
  Result := 0;
  for i := 0 to Length - 1 do
    Result := Result xor Pdata[i];
end;
...
Рейтинг: 0 / 0
17.09.2021, 09:51
    #40098214
DmSer
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
автор with TimeOuts do begin
{ Понял, что это тайминги (отметил их в комментарии выше), не знаю нужно-ли их вообще трогать здесь, подскажите пожалуйста}

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

end;


Тайминги нужны, а как же без них!

Но думаю, должно быть как-то так (кто этим пользуется, поправьте меня, если я не прав!):
Код: pascal
1.
2.
3.
4.
5.
6.
7.
 with TimeOuts do begin
   ReadIntervalTimeout         := 1000;
   ReadTotalTimeoutMultiplier  := 0;
   ReadTotalTimeoutConstant    := 0;
   WriteTotalTimeoutMultiplier := 0;
   WriteTotalTimeoutConstant   := 0;
 end;
...
Рейтинг: 0 / 0
17.09.2021, 10:47
    #40098232
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
скелА,

1. Ты, конечно, переводишь всё в байты перед посылкой, но это не нужно, используй сразу AnsiString/AnsiChar, раз уж склеиваешь команду, и его же и передавай. В переводе ответа в юникод я тоже смысла не вижу.

2. ReadFile/WriteFile в синхронном режиме (как у тебя) будут зависать навечно, ожидая выполнения.
Необходимо использовать Overlapped структуру. И WaitCommEvent. Иначе корректно работать будет только иногда и случайно.
...
Рейтинг: 0 / 0
17.09.2021, 10:50
    #40098233
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
скелА
Код: sql
1.
4 параметра ниже есть в спецификации, но не понятно, как можно понимать "Even", это 0 или нечто иное

Все параметры описаны в документации по функции SetCommState
...
Рейтинг: 0 / 0
17.09.2021, 13:44
    #40098301
DmSer
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
YuRock
скелА,

2. ReadFile/WriteFile в синхронном режиме (как у тебя) будут зависать навечно, ожидая выполнения.
Необходимо использовать Overlapped структуру. И WaitCommEvent. Иначе корректно работать будет только иногда и случайно.


Я кстати так же думал, однако функция SetCommTimeouts должна решать эту проблему.
...
Рейтинг: 0 / 0
17.09.2021, 15:39
    #40098342
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
DmSer
YuRock
скелА,

2. ReadFile/WriteFile в синхронном режиме (как у тебя) будут зависать навечно, ожидая выполнения.
Необходимо использовать Overlapped структуру. И WaitCommEvent. Иначе корректно работать будет только иногда и случайно.


Я кстати так же думал, однако функция SetCommTimeouts должна решать эту проблему.
Возможно. Не решал её таким образом.
...
Рейтинг: 0 / 0
17.09.2021, 16:17
    #40098360
скелА
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
YuRock, Попробую, вдруг прокатит..
...
Рейтинг: 0 / 0
17.09.2021, 16:18
    #40098362
скелА
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
Забыл прикрепить исходник
...
Рейтинг: 0 / 0
17.09.2021, 16:19
    #40098363
скелА
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
Соколинский Борис, Понял, спасибо!

p.s
Стал искать откуда ноги ростут у "absolute", думаю простые любители в целом редко им пользуются и нарыл в далеком 2005 такую
ветку на форуме, там ответчики интеллигентно стебутся над автором, но общий дух разглагольствований со всеми "заморозками" и
"кристаллической структурой" наталкивает на некий DANGER в ее использовании. Так-ли это, или лучше все-же найти некий аналог..
Вообщем хотелось-бы чтобы все работало)
http://delphimaster.net/view/14-1125268629/all
...
Рейтинг: 0 / 0
17.09.2021, 16:21
    #40098365
скелА
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
Большое спасибо за ценные замечания, сейчас сяду купировать все это хозяйство..
...
Рейтинг: 0 / 0
17.09.2021, 16:44
    #40098369
скелА
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
Запамятовал уточнить.. InQueue, OutQueue в 2048 не аукнится ?

Код: pascal
1.
2.
3.
4.
5.
6.
 // не удалось открыть порт, - лог и выход
 if not openCom(s_com, 2048, 2048, 9600) then
 begin
   f_.lg.lines.add('Error in "opencom(..)"');
   EXIT
 end;
...
Рейтинг: 0 / 0
17.09.2021, 16:58
    #40098370
скелА
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
авторTransmission Rate - 9600 bps
Character Length - 8 bits
Parity bits - Even
Stop bits - 1 stop bit
Flow Control - None


И вот это "Flow Control = None" - т.е видимо, тоже должен быть отд.флаг для DCB (в функции SetCommStatus), если не ошибаюсь
...
Рейтинг: 0 / 0
17.09.2021, 17:10
    #40098372
s62
s62
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
YuRock
DmSer
пропущено...


Я кстати так же думал, однако функция SetCommTimeouts должна решать эту проблему.
Возможно. Не решал её таким образом.
Можно, я написал n-ное количество программ с обменом по RS-232 в синхронном режиме, и они нормально работали у клиентов. Но там железки или отвечали в течение короткого времени (в среднем 60 мс многие из них), или были случаи, когда сами постоянно слали пакеты о своем состоянии, посылаешь ты им команды или нет.
У ТС ожидание ответа может быть до 90 сек.
авторDelay to send Response after Command: 0 - 90 sec Видимо, когда устройство выдаст купюры, тогда отчитывается. Тут на мой взгляд стоит использовать конечно асинхронное чтение. Или же делать тупой поллинг с разумным таймаутом.

скелА,
вот вам еще пара советов про работу с портом.
Запись/структуру DCB можно не заполнять самому всю, а сначала получить её текущее состояние, потом изменить нужные поля и потом задать их. Т.е. Сначала вызываете GetCommState (он выдаст DCB с текущими параметрами), потом меняете нужные параметры, а потом вызываете SetCommState.

Протокол обмена, как вы описали, бинарный, причем структура запроса и ответа для команды "DISPENSE", которую вы посылаете, известна. На мой взгляд тут нет никакой необходимости преобразовывать это все в Char и строки. Байты задаёте, байты посылаете, байты получаете и байты интерпретируете.
Можно объявить Packed record со структурой данных как в запросе и как в ответе и передавать в функции WriteFile и ReadFile их, мне кажется, так проще и нагляднее. Но можно этого и не делать, просто использовать массивы байтов.
...
Рейтинг: 0 / 0
17.09.2021, 17:56
    #40098385
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
s62 wroteТут на мой взгляд стоит использовать конечно асинхронное чтение. Или же делать
тупой поллинг с разумным таймаутом.

Или выделить отдельный поток, который только и делает, что читает. Как раз на
случай неожиданных посылок по инициативе самого устройства (например, нажатия
кнопки на нём).
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
17.09.2021, 18:16
    #40098388
скелА
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
Понял, благодарю за подробный ответ, действительно так проще и вероятности на ошибку будет наверно меньше, с GetCommState имею ввиду, вместе (с нормальным бинарным представлением в коде), но тут больше вопрос удобства внешнего восприятия кода, как я понимаю, т.е. работать оно должно и так.

Да, там именно так все и устроено, диспенсер перестраховывается, удастся ему выдать эти "клочки" или нет (понял что не самое лучшее состояние бумаги)
Тут насколько я понимаю "узкое" место с кучей итераций для ReadFile будет именно до получения того единственного байта успеха ACK (0x06) от диспенсера.

По идее, когда он его вернул, сообщив что все в порядке и команда принята к рассм.(readACK = ReadFile(F, Temp, 1, 1, nil) в таймере) - остается еще максимум 90 сек. на ответ.
Отсюда вопрос, безопасней ли будет, с точки зрения всех этих подводных течений с синхронизацией, выставить таймер на эти 90 сек. и просто проверить по истечении этого максимума, пришел-ли основной пакет в буфер.
Ну т.е имею ввиду, - он ведь там останется, никуда не перезапишется за это время, с учетом, что устройство по протоколу не должно более ничего посылать,
пока я не отправлю ему новую порцию данный (пакет). Вообщем поправте, если ошибаюсь, давненько не работал с API ..
...
Рейтинг: 0 / 0
17.09.2021, 18:17
    #40098389
скелА
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
Dimitry Sibiryakov, Ага, все-таки может значит, что-то отправить без запросов..
...
Рейтинг: 0 / 0
17.09.2021, 18:24
    #40098390
s62
s62
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
Dimitry Sibiryakov,
я не писал, но да, что синхронное чтение, что асинхронное с WaitCommEvent, как YuRock написал, стоит делать в отдельном потоке. Я правда писал синхронный обмен и в основном потоке, но по неопытности, плюс быстрый ответ устройства не создавал проблем.
...
Рейтинг: 0 / 0
17.09.2021, 18:35
    #40098392
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
скелААга, все-таки может значит, что-то отправить без запросов..

Может и не может, в стартовом посте об этом не упомянуто. Но странно было бы
видеть банкомат без кнопок выдающий купюры исключительно по пинку "изнутри".
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
17.09.2021, 20:33
    #40098394
defecator
Модератор форума
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
а почему бы ТС не взять какую-то готовую реализацию библиотеки для работы с ком-портом ?
разве что ему не задачу надо решить, а поизучать возможности апи с созданием своего велосипеда
...
Рейтинг: 0 / 0
18.09.2021, 00:17
    #40098404
скелА
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
defecator, в приоритете конечно стабильность, пусть даже придется сделать вполне приличный скутер из этого самоката, - тобишь даже если кода увеличится в-разы.. Буду безмерно признателен за такую специфичную библиотеку
...
Рейтинг: 0 / 0
18.09.2021, 01:34
    #40098412
northener
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
скелА
defecator, в приоритете конечно стабильность, пусть даже придется сделать вполне приличный скутер из этого самоката, - тобишь даже если кода увеличится в-разы.. Буду безмерно признателен за такую специфичную библиотеку

Из классики TPApro .
...
Рейтинг: 0 / 0
18.09.2021, 12:51
    #40098443
скелА
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с COM. Отправка одной команды (пакета) и получение ответа
northener, Спасибо, кажется идеально.. Ставлю.

Выдержка:
Особенно порадовало -

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

Пакет данных можно рассматривать как расширенный триггер данных. Пакеты автоматически собирают данные из входящего потока данных на основе критериев, указанных в свойствах компонента пакета данных, и доставляют данные, когда критерии соблюдены. В отличие от традиционных триггеров данных, пакеты данных выполняют собственную буферизацию. Это означает, что пакеты данных не имеют тех же ограничений, что и триггеры данных (эти данные могут больше не быть доступны во входном буфере для обработки при срабатывании триггера данных).

Обычно вы используете пакеты данных вместо триггеров данных, когда данные, которые вы ищете, имеют фиксированную длину или начинаются или заканчиваются известной строкой данных. Эти условия могут быть установлены в компоненте пакета данных во время разработки или выполнения.
...
Рейтинг: 0 / 0
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Работа с COM. Отправка одной команды (пакета) и получение ответа / 24 сообщений из 24, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]