ComPort.WriteStr - dziwne zachowanie

2

Wtrącę trochę do tematu.
Swego czasu skonstruowałem tzw. wyświetlacz klienta. Gdy oprogramowanie drukuje paragon w trybie tzw. offline to klient na bieżąco nie wie jaka jest wartość paragonu. Właśnie do tego stosuje się wyświetlacze klienta. Prosta sprawa, wyświetlacz LCD 2x20 znaków i jakieś sterowanie.
Oczywiście nie wymyślałem wszystkiego od podstaw i odnalazłem dokumentacje popularnego wyświetlacza IBM, moje oprogramowanie emuluje ten właśnie protokół a jest on prosty. Zawsze wysyłane są 40 znaków, sterownik po prostu 21 i następne znaki umieszcza w nowej linii i to wszystko.

Sprawa była bardzo prosta, użyłem najtańsze Arduino NANO z portem USB na Atmedze 328 i kontrolerem CH340 i jak wysyłałem na nie 40 znaków z terminala wszystko działało. Jednak mój program używający komponentu ComPort nie działał? Jednak uwaga, gdy oprogramowanie wgrałem do Arduino Pro Micro na Atmedze 32U4 (która ma kontroler USB w sobie) wszystko działało i z terminala i z komponentu ComPort.
Dla zbadania sprawy nawet zrobiłem analizę ramki wysyłanej przez komputer na oscyloskopie (który potrafi dekodować ramki COM) i nie znalazłem różnicy. Zdekodowana ramka wyglądała tak samo. Chociaż nie przesądza to że fizycznie może ona wyglądać troszkę inaczej, ciężko jednak to analizować na domowym sprzęcie.

W każdym razie, jeśli do sterowania tego robota używasz jakieś Andino, zmień na próbę na inne.
Następna sprawa, jakiej wersji ComPorta używasz, jest tego od groma na rynku i może pomyśl nad zmianą na inny komponent.

0

Mam zainstalowaną ver. 4.11 ComPortu. Zastanawiam się nad zmianą komponentu i dlatego zapytałem jakich używacie.

Jak już pisałem, też zauważyłem, że mimo identycznych ramek i otwierania portu, jeśli łączę się z wirtualnym portem utworzonym w mikrokontrolerze to pojawiają się problemy.

0

Też używałem wersji 4.11f ale pamiętaj że to jest komponent z 2011 roku. Autor obiecywał że w wersji 4.20 poprawi dużo zauważonych błędów i są pogłoski że taka wersja nawet została napisana, niestety nigdy nie została opublikowana. W wersji tej także miał się przyłożyć do komunikacji USB - niby to samo ale jednak.
Jakiś czas temu znalazłem na github-ie projekt ComPort-Library i już się ucieszyłem że projekt nadal żyje https://github.com/CWBudde/ComPort-Library
Okazało się jednak że chłopak który "przejął" projekt z sourceforge tak naprawdę tylko dostosowuje go do nowych wersji Delphi. Dobre i to ale jednak potrzebne dzisiaj jest coś więcej.

Dlatego przyglądam się projektowi AsyncPro https://github.com/TurboPack/AsyncPro
Nawet robiłem z nim jakieś testy i nie sprawia problemu. Jednak na razie boję się użyć tego komponentu w rozwiązaniach produkcyjnych gdzie musi po prostu działać.

Może trzeba już zaryzykować i np. wrzucić do projektu dwa sposoby komunikacji. Gdy padnie AsyncPro przełączę się na ComPorta i już.

A może wy macie jakieś spostrzeżenia co do tego komponentu (w końcu jest do pobrania w GetIt)?

Ps. Mirek z firmy Atnel swego czasu napisał komponenty do komunikacji po RS232 a później nawet bezpośrednio USB. Komponenty są płatne i chyba nawet nieźle działają ale problem jest taki że kolega Mirek za bardzo nie nadąża za aktualizacją do nowych wersji Delphi.

3

Czy naprawdę potrzeba Wam tych wszystkich zaawansowanych rzeczy które dostarczają te komponenty? Używacie asynchronicznego wysyłania/odbierania danych? Jeśli nie to naprawdę nie rozumiem w czym problem żeby sprawdzić czy te parę linii kodu nie wywoła błędu:

  int iCom = 12;
  HANDLE hCom = INVALID_HANDLE_VALUE;

// łączenie
  char szCom[100];
  sprintf(szCom,"\\\\.\\COM%d",iCom);
  hCom = CreateFile(szCom,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,NULL,NULL);

  if (hCom == INVALID_HANDLE_VALUE)
  {
    return;
  }
// inicjowanie parametrów transmisji
  DCB dcb;
  bool bSuccess;

  SecureZeroMemory(&dcb, sizeof(DCB));
  dcb.DCBlength = sizeof(DCB);

  bSuccess = GetCommState(hCom, &dcb);

  if (!bSuccess)
  {
    return;
  }

  dcb.BaudRate = CBR_9600;
  dcb.ByteSize = 8;
  dcb.Parity   = NOPARITY;
  dcb.StopBits = ONESTOPBIT;

  SetCommState(hCom, &dcb);
// wysylanie
  char buff[] = "test";
  unsigned long Res;
  WriteFile(hCom,buff,strlen(buff),&Res,NULL);

Niestety kod jest w C, ale w Delphi będzie wyglądać podobnie (nie ma tu nic trudnego do zamiany, same typy proste i funkcje z WinAPI). Ten kod nie ma prawa nie działać. Jest to wersja pozbawiona obsługi błędów tego co siedzi u mnie na produkcji (nie próbowałem tego kompilować, więc mogą być drobne błędy, jednak widać czego należy użyć do tego zadania). Jedyne problemy miałem z wysyłaniem po źle ustawionych parametrach transmisji. Wtedy o dziwo wszystko się powodziło bez błędów jednak urządzenie docelowe nic nie dostawało/reagowało.

Zamknięcie tego we własnej klasie, a nie używanie do tego celu komponentu (i tak nie kładę takich komponentów na formatce, a tworzę w runtime) ma taką zaletę, że nie muszę nic instalować i zaśmiecać tysiącami komponentów do drobnych zadań. Dodatkowo inny programista też w łatwy sposób skompiluje mój system. Co więcej przy zmianie wersji IDE na nową zazwyczaj nic nie muszę zmieniać i kod się od razu kompiluje.

1

Na pytanie @furious programming odpowiem jednak w nowym poście, a nie w komentarzu. Ponieważ znalazłem też coś interesującego. Udało mi się znaleźć taki artykuł odnośnie komunikacji po COM'ach w Delphi http://sheepdogguides.com/dt4q.htm Jak widać radzą jeszcze ustawić timeouty transmisji.

function TDD80f1.SetUpSerPort:byte;
//Call AFTER establishing hCommFile. Returns 0 if setup goes okay.

var
   DCB: TDCB;
   Config : string;
   CommTimeouts : TCommTimeouts;
   bErrCode:byte;

begin
bErrCode:=0;//Will eventually be returned. If zero, no error seen
if not SetupComm(hCommFile, RxBufferSize, TxBufferSize) then
   bErrCode:=1;
   { Raise an exception --- these comments
       all scraps of source material... will try to use exceptions in
       due course!}

if bErrCode=0 then //no "begin" here...  (this is part of kludge avoiding exceptions)
if not GetCommState(hCommFile, DCB) then
   bErrCode:=2;
   { Raise an exception }

Config := 'baud=9600 parity=n data=8 stop=1' + chr(0);

if bErrCode=0 then //no "begin" here...  (this is part of avoiding exceptions)
if not BuildCommDCB(@Config[1], DCB) then
   bErrCode:=3;
   { Raise an exception }

if bErrCode=0 then //no "begin" here...  (this is part of avoiding exceptions)
if not SetCommState(hCommFile, DCB) then
   bErrCode:=4;
   { Raise an exception }

if bErrCode=0 then //no "begin" here...  (this is part of avoiding exceptions)
with CommTimeouts do
begin
   ReadIntervalTimeout := 0;
   ReadTotalTimeoutMultiplier := 0;
   ReadTotalTimeoutConstant := 200;//This determines(?) how long
      //you stay in an attempt to read from serial port. milliseconds
      //I hope these routines are reading from a buffer managed by
      //the OS independently of these routines.
   WriteTotalTimeoutMultiplier := 0;
   WriteTotalTimeoutConstant := 1000;
end;

if bErrCode=0 then //no "begin" here...  (this is part of avoiding exceptions)
if not SetCommTimeouts(hCommFile, CommTimeouts) then
   bErrCode:=5;
   { Raise an exception }

result:=bErrCode;
end;//function SetUpSerPort:byte;

Jak się zagłębić w stronę więcej jest też co nie co o Arduino i innych tego typu rzeczach.

Co do pytania @furious programming to niestety z uzyciem Write, WriteLn, Read i ReadLn na pliku portu jest pewien problem. Zasadniczo powinno się dać w ten sposób pisać do portu (jak również mapując port jako strumień), jednak nie da się w ten sposób ustawić parametrów transmisji. Pod Windows jedyną możliwością ustawienia parametrów transmisji z poziomu WinAPI jest użycie funkcji SetCommState. Ona wymaga otwarcia portu COM i przekazania uchwytu do portu. Jednak nie istnieje przeciwwskazanie do tego aby otworzyć port za pomocą CreateFile ustawić parametry transmisji za pomocą SetCommState oraz zamknąć port za pomocą CloseFile. Po tym parametry portu COM są niezmienione do czasu aż jakiś inny program nie zmieni ustawień, albo nie zresetujemy komputera. Zatem wydaje się być możliwym użycie AssignFile na porcie COM. Jeśli parametry transmisji będą ok wszystko powinno działać. Największym niestety problemem jest ustawienie prędkości, parzystości oraz bitów stopu. Zatem jeśli byśmy mieli ustawiony poprawnie port, to metoda o którą pytasz powinna zadziałać.

0

Dzisiaj zebrałem jeszcze trochę siły. Łączę się między laptopem, a robotem przez przejściówkę USB-COM i jest OK.
Ale jak na chwilę, dla testu, wyjmę z USB tą przejściówkę to pojawia się błąd nr 5 i od tego momentu już nie mogę nawet zamknąć aplikacji napisanej z ComPort. Muszę zamykać przez ubicie tego procesu.
Dlaczego tak się dzieje, że już nie da się nawet zamknąć programu?

Mam jednak za mało wiedzy, aby tworzyć własne komponenty (przez klasy, itp), więc muszę oprzeć się na waszej wiedzy.
Sprawdziłem ComPort z różnymi timeoutami i dalej pojawia się ten błąd nr. 5.
Macie pomysły jak zneutralizować ten błąd?

Fajnie jakby można było zainstalować w pełni działający komponent, ale @robertz68 mówi że AsyncPro ma jeszcze więcej błędów, więc już sam nie wiem czy jest gdzieś dobrze działający komponent.
A może znacie takie komponenty do transmisji COM?

1
Markoni napisał(a):

Dzisiaj zebrałem jeszcze trochę siły. Łączę się między laptopem, a robotem przez przejściówkę USB-COM i jest OK.
.....

Problem stary jak Świat, niestety nie rozwiązany kompleksowo do dzisiaj.
Jak pisałam gdzieś kiedyś, autor komponentu pisał go z myślą o fizycznych portach com, które dość ciężko odłączyć od komputera, szczególnie w trakcie działania. Niestety wirtualne porty to inna bajka a autor zakończył wsparcie lata temu. Jak też gdzieś pisałem, miał już opracowane rozwiązanie ale go nie opublikował wraz z nową wersją. Szczerze, to nie wiadomo jak by działało bo jednak obsługa wyjątków w comport kuleje dość mocno.

Społeczność jednak nie odpuszczała i starała się jakoś rozwiązać problem. Niestety chyba za dobrze to nie wyszło ale przynajmniej są sposoby żeby zignorować błąd i móc dalej używać aplikację.
Tutaj jest jeden ze sposobów polegających na modyfikacji oryginalnego pliku CPort.pas > https://sourceforge.net/p/comport/discussion/261327/thread/c7ab4c4a/

0

Jeszcze zapytam, z czym związany jest komunikat?:

#err0041 GetNewComInfo Exception
0

Do Delphi nie znalazłem dobrego komponentu, odradzam ComPort bo ma błędy i się wysypuje, przynajmniej tak było kiedyś. Żeby to zaczęło dobrze działać komponent napisałem sam co też wam polecam. Niestety nie mogę udostępnić napisanego przeze mnie.

0 użytkowników online, w tym zalogowanych: 0, gości: 0