ComPort i usunięte urządzenie USB

ComPort i usunięte urządzenie USB
robertz68
  • Rejestracja:około 18 lat
  • Ostatnio:około 17 godzin
  • Lokalizacja:Zielona Góra
0

Cześć,

jakiś czas temu napisałem program który między innymi łączy się z prostym wyświetlaczem LCD. Wyświetlacz skonstruowałem na Arduino i podłączam go do komputera kabelkiem USB. Doinstalowuje się w tym czasie wirtualny port COM (sterowniki od Arduino).

Wszystko działa idealnie gdy istnieje port COM, nawet start programu bez portu COM nie robi problemów, po prostu brak tego portu na starcie aplikacji wyłącza tą funkcjonalność.

Problem jest bardzo duży gdy port COM istnieje w trakcie startu programu a później znika, niestety jako że jest to USB a obsługa jest zdolna do wszystkiego takie coś może się zdarzyć.
Dostaję wtedy wyjątek który jest nie do "ubicia". Jedyne wyjście to zabicie procesu w Menadżerze zadań co jest zbyt skomplikowane dla obsługi - oni po prostu wyłączają komputer.

Konkrety, używam komponentu ComPort Library https://sourceforge.net/projects/comport/
Problem jest znany ale nie do końca rozwiązany. Większość rozwiązań sugeruje lekką modyfikację kodu w źródle komponentu a konkretnie w procedurze TCustomComPort.AbortAllAsync.

Rzeczywiście, taka podmiana powoduje że program pomimo wyskakujących komunikatów o błędzie pozwala się (z trudem ale jednak) zamknąć. Ale to nie jest przecież rozwiązanie.

Problem z komponentem jest taki że po prostu nie rozpoznaje on że nie ma już portu COM, zdarzenie onError albo onException się nie zdarza chyba nigdy. To by rozwiązało problem.

Może ktoś z was już pokonał ten problemik i podzieli się uwagami albo ma jakąś sugestię?

flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 12 godzin
  • Lokalizacja:Tuchów
  • Postów:12166
1

Nie używałem tego komponentu, ale pierwsza myśl jaka mi się nasunęła, to łapanie komunikatu WM_DEVICECHANGE i sprawdzanie czy port istnieje. Jeśli nie istnieje to zwolnij obiekt portu, a jak się pojawi to go utwórz.

W przypadku napotkania wyjątku próbuj zwolnić obiekt (poza kodem komponentu), a jeśli ten będzie się stawiał i rzucał kolejnymi wyjątkami, próbuj go ubijać w pętli – dotąd aż przestanie pyskować. ;)


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE
cerrato
Moderator Kariera
  • Rejestracja:około 7 lat
  • Ostatnio:około 6 godzin
  • Lokalizacja:Poznań
  • Postów:8769
0

Tak sobie myślę - a jakby ten kod odpalić w wątku i w razie problemów po prostu ubić ten wątek? Taki pomysł mi przyszedł do głowy, ale gwarancji czy to ma sens udzielić nie mogę ;)


flowCRANE
Chyba wyjdzie na to samo co w przypadku ubijania obiektu. Najważniejsze aby nie pozwolić aby wyjątek uciekł spod kontroli, bo wtedy apka się wykrzaczy. A to jak się za to zabrać to już kwestia poboczna. :P
cerrato
No nie do końca to samo. W pierwszym poście OP napisał Jedyne wyjście to zabicie procesu w Menadżerze zadań co jest zbyt skomplikowane dla obsługi - oni po prostu wyłączają komputer - więc moim zdaniem, jakby problematyczny kod dać do osobnego wątku, to będzie możliwość ubicia przez program tylko tego wątku, więc nie będzie konieczności angażowania użytkownika. Wiadomo, że lepiej zrobić to porządnie - w jakiś sposób wykryć zniknięcie portu i natychmiast zawiesić komunikację, ale jeśli mówimy o rozwiązaniach doraźnych, to wątek może problem rozwiązać.
flowCRANE
No tak, tyle że wątek nie jest konieczny, bo wyjątki można łapać w głównym wątku i je kasować, nawet jeśli komponent wypluje ich milion pod rząd. Wystarczy łapanie wyjątków zapętlić i problem z głowy. A raczej zamiecenie problemu pod dywan, no ale lepsze to niż crash programu. Przy czym to i tak nie ma za bardzo sensu, skoro użycie portu COM jest banalne, a więc dodatkowe komponenty nie są praktycznie do niczego potrzebne.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 12 godzin
  • Lokalizacja:Tuchów
  • Postów:12166
2

Można też olać komponenty i po prostu skorzystać z WinAPI – nie będzie żadnych wyjątków, co najwyżej liczbowe kody błędów do sprawdzania i reagowania na zaistniałe problemy. To pozwoli na implementację stabilnego rozwiązania.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE
MA
  • Rejestracja:ponad 16 lat
  • Ostatnio:19 dni
0

A co za problem, mając do dyspozycji kod źródłowy, dodać sprawdzanie istnienia wybranego portu przed zapisem, czyszczeniem bufora in/out, bądź zamknięciem?

robertz68
  • Rejestracja:około 18 lat
  • Ostatnio:około 17 godzin
  • Lokalizacja:Zielona Góra
0
marogo napisał(a):

A co za problem, mając do dyspozycji kod źródłowy, dodać sprawdzanie istnienia wybranego portu przed zapisem, czyszczeniem bufora in/out, bądź zamknięciem?

oj chyba jednak jakiś problem jest skoro przez ponad 10 lat autor komponentu nie poradził sobie z tym. Według wpisów na sourceforge niestety nie przyłożono się do obsługi błędów w tym komponencie. Lata temu autor napisał że naprawił błąd w wersji 4.20 ale jej nie upublicznił (ostatnia wersja to 4.11f).
Znalazłem jednak forka tego komponentu na githubie i widać że coś się tam dzieje, przede wszystkim powstają wersje na nowe Delphi. Błąd jednak pozostał :).

Cały czas kombinuję, na teraz najbardziej skłaniam się do podpowiedzi @furious programming aby złapać komunikat błędu WinApi, chociaż inne rozwiązania w stylu zmienna wskazująca czy port istnieje także wchodzi w grę.

flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 12 godzin
  • Lokalizacja:Tuchów
  • Postów:12166
0

Teoretycznie @marogo ma rację – jeśli masz kod źródłowy komponentu, to masz możliwość sprawdzenia z których funkcji WinAPI kontrolka korzysta i wywalić kod odpowiedzialny za generowanie wyjątków. Wszystkie funkcje z systemowej biblioteki bazują na liczbowych kodach błędów, więc siłą rzeczy muszą być tam instrukcje rzucające wyjątki, gdy dana funkcja z WinAPI zwróci numer błędu.

W razie czego zawsze możesz sprawdzić jak wygląda kod klasy TLazSerial, która służy do tego samego. A jak chcesz coś innego to możesz też otworzyć plik o nazwie swojego portu i do niego pisać jak do normalnego pliku. Możesz to wykonać za pomocą podstawowych instrukcji, ale też za pomocą klasy TFileStream. Albo gołe WinAPI.

Sprawdzenie czy port istnieje to nic innego jak sprawdzenie czy istnieje plik o nazwie portu.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE
KA
  • Rejestracja:prawie 20 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Gorlice
1

Nie korzystałem z tego komponentu ale jeżeli wiesz, że wyjątek powoduje procedura AbortAllAsync to tam jest jawnie wyrzucany wyjątek jeżeli funkcja PurgeComm się nie powiedzie a jak ma się powieść skoro uchwyt wskazuje na urządzenie którego już nie ma więc właśnie przed if not PurgeComm(FHandle, PURGE_TXABORT or PURGE_RXABORT) then powinieneś sprawdzać czy port istnieje czyli zwykłe sprawdzenie czy plik istnieje i jeżeli nie to nie wykonywać dalszej części kodu procedury tylko dopisać swoje własne zdarzenie w którym zareagujesz na taką sytuację zamiast wywoływać wyjątek.


Nie odpowiadam na PW w sprawie pomocy programistycznej.
Pytania zadawaj na forum, bo:
od tego ono jest ;) | celowo nie zawracasz gitary | przeczyta to więcej osób a więc większe szanse że ktoś pomoże.
flowCRANE
No i właśnie o to chodzi – nie ma sensu kombinować.
MA
  • Rejestracja:ponad 16 lat
  • Ostatnio:19 dni
3
robertz68 napisał(a):
marogo napisał(a):

A co za problem, mając do dyspozycji kod źródłowy, dodać sprawdzanie istnienia wybranego portu przed zapisem, czyszczeniem bufora in/out, bądź zamknięciem?

oj chyba jednak jakiś problem jest skoro przez ponad 10 lat autor komponentu nie poradził sobie z tym.

Z takim podejściem ,to do dziś by nie powstało koło i wiele innych rzeczy ;-)

Kopiuj
function TCustomComPort.ComPortExists(Port: string): Boolean; //sprawdza, czy port istnieje
 Var
  Size: Cardinal;
  CommConfig:TCommConfig;
begin
  result := false;
  FillChar(CommConfig, SizeOf(TCommConfig), 0);
  CommConfig.dwSize := SizeOf(TCommConfig);
  Size := SizeOf(TCommConfig);
  if GetDefaultCommConfig(PChar(Port), CommConfig, Size) then //próba odczytu konfiguracji portu (! bez prefixu \\.\)
    result := (CommConfig.dcb.BaudRate > 0); //jeśli bitrate nie jest 0, to znaczy że port istnieje w systemie
end;

procedure TCustomComPort.Close;
begin
  //.....
  if ComPortExists(Port) then
    AbortAllAsync; 
  //.....
end;

procedure TCustomComPort.ClearBuffer(Input, Output: Boolean);
var
  Flag: DWORD;
begin
  //...
  if ComPortExists(Port) then
    if not PurgeComm(FHandle, Flag) then
  //...
end;

Przy próbie otwarcia portu, funkcja CreateFile pozwoli wykryć, czy wybrany port istnieje (albo da się otworzyć, albo nie).
Przy zapisie/odczycie nie koniecznie trzeba sprawdzać istnienie portu (przy wielu operacjach zapisu/odczytu spowolniłoby ten proces), bo załatwią to funkcje odpowiednio WriteFile/ReadFile.
Zwolnieniem zasobów przyznanych na otwarty port, który "zniknął", zajmie się metoda DestroyHandle.
Dodatkowo, jak napisał @furious programming, wskazane jest "łapanie komunikatu WM_DEVICECHANGE i sprawdzanie czy port istnieje" wykorzystując metodę ComPortExists, jeśli chciałbyś np. powiadamiać użytkownika, że pociągnął za kabel i program utracił połączenie z urządzeniem. No i wtedy na końcu zamykasz otwarty port.

robertz68
  • Rejestracja:około 18 lat
  • Ostatnio:około 17 godzin
  • Lokalizacja:Zielona Góra
3

przyznam że kilkukrotne użycie klasy Exception trochę rozwiązało problem ale jako że podrzuciliście kilka ciekawych podpowiedzi to czuję się zobowiązany to sprawdzić.
Jutro wszystko potestuję i dam znać.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)