[Linux][Pascal] Lista (naprawdę) dostępnych portów szeregowych

Tutaj umieszczamy tematy związane z językami programowania niepasującymi do innych działów.
Regulamin forum
Temat prosimy poprzedzić nazwą języka umieszczonego w nawiasach kwadratowych np. [Pascal].
Awatar użytkownika
tasza
Geek
Geek
Posty: 1082
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

[Linux][Pascal] Lista (naprawdę) dostępnych portów szeregowych

Postautor: tasza » czwartek 17 sty 2019, 22:49

♬ ☘ Moja muzyka do kodowania ♬ ♬ ♬ ☘
♫ ♩ ♪ Nachtblut ⚡ ☘ ⚡ Antik♪ ♩ ♫
https://youtu.be/4msVueJK_6I



Swego czasu na łamach Elektroniki dla Wszystkich pozwoliłam sobie na stwierdzenie, że jak do czegoś dają kod źródłowy to nie tylko wolno, ale wręcz trzeba coś w nim dłubnąć. Zwyczaj patroszenia nowych zabawek został mi z dzieciństwa, stąd dorwawszy kawałek fajnej terminalowej aplikacji w Pascal nie omieszkałam zbudować programiku na Linux, a potem wściubić nos w środek. No i jest tak...


multi-kulti

Środowisko Lazarus IDE (jak i działający w tle Free Pascal) tworzone są pod hasłem - napisz raz, kompiluj wszędzie. No super takie ideały są i w znakomitej większości przypadków fakt skompilowania się kodu zachodzi i w Windows i w Linux. Z działaniem jest deko gorzej, ponieważ nie ma cudów - są funkcjonalności mocno wykorzystujące specyficzne cechy systemu operacyjnego, a im bliżej schodzimy sprzętu, tym prawdopodobieństwo ich spotkania jakby większe.

Do takich właśnie zagadek należy temat listy wyboru nazwy portu szeregowego przy uruchamianiu czy konfigurowaniu aplikacji. Czasy wbudowanych w maszynę portów COM zaczynają odchodzić w przeszłość, królują wszelkie wtykalne przelotki i inne cuda na USB, dla użytkownika oznacza to, że lista portów nie jest stała, zmienia się zarówno ilościowo jak i dostępnym spektrum nazw.
A to z kolei oznacza, że aplikacja powinna wspomóc użytkownika i wylistować aktualnie dostępne porty, niech sobie biedny człowiek wybierze.

W środowisku Lazarus, w całkiem fajnych klockach synapse sprawę rozwiązano przygotowując funkcję GetSerialPortNames, implementacja objęta jest kompilacją warunkową. Wersja dla Windows bazuje na rejestrze, wersja dla Linux listuje zasoby kartoteki /dev. Jeden z wariantów dostępnych w sieci znajdziemy w pliku: :arrow: https://github.com/marado/synapse/blob/ ... .pas#L2320
Wskazuję implementację linuksową, ponieważ do niej miałam pewne zastrzeżenia, w szczególności - EAccessViolation w przypadkowych momentach, stąd nieodparta potrzeba napisania własnego rozwiązania, które przy okazji przetestowałam na podesłanej aplikacji.

Środowisko sprzętowe to dwie przelotki RS232/USB na układach Prolific, oryginalne Arduino oraz Ardu z Chinowa z układem CH341, do tego Hub USB.

00_IMG_4991.JPG


Dla Windows - całość pracuje całkiem sensownie - lista COM zgadza się z tym co wypisuje Manager Urządzeń.

01_ser_win.png


Dla Linux, po drobnych zabiegach też wszystko działa jak należy:

02_ser_lin.png
03_ser_detail.png


łatka szeregowa

I proszę, oto moja wieczorna wizja, jak załadować rozwijaną listę wyboru (combobox) kolejnymi nazwami portów szeregowych. Przyjęłam, że pokazywane mają być porty z płyty (ttySnn), kabelki USB (ttyUSBnn) oraz wszelkie autorskie szeregowe interfejsy znane z Ardu i niektórych modemów GSM czyli ttyACMnn. Btw, ciekawe info o różnicach w dwóch ostatnich znajdziemy tu: :arrow: https://rfc1149.net/blog/2013/03/05/wha ... evttyacmx/

Funkcyjna pomocnicza sprawdzająca faktyczną dostępność wskazanego nazwą portu:

Kod: Zaznacz cały

{$IFDEF LINUX}
function IsPortAvailable ( portName : string ) : boolean;
var
  aPort : TBlockSerial;
begin
  aPort := TBlockSerial.Create;
  aPort.Connect( portName );
  IsPortAvailable := aPort.LastError = 0;
  aPort.Free; // spoczko, destruktor robi close
end;
{$ENDIF}


Funkcja budująca listę dostępnych do wykorzystania portów:

Kod: Zaznacz cały

{$IFDEF LINUX}
function GetSerialPortNamesEx : string;
var
  serialPorts : TStringList;
  i : integer;
  res : string;
begin
  res := '';
  serialPorts := TStringList.Create;
  try
    FindAllFiles( serialPorts, '/dev', 'ttyS*;ttyUSB*;ttyACM*', false );
    for i := 0 to serialPorts.Count-1 do
    begin
       if IsPortAvailable( serialPorts.Strings[i] ) then
       begin
         if length( res ) <> 0 then res := res + ',';
         res := res + serialPorts.Strings[i];
       end;
    end;
  finally
    serialPorts.Free;
  end;
  GetSerialPortNamesEx := res;
end;
{$ENDIF}


Powyższe zwróci nam te nazwy, które możemy zaproponować w danej chwili użytkownikowi do wyboru, jest oczywiste, że lista może zdarzyć się pusta, musimy sobie i z tym poradzić.

Ładowanie nazw portów do listy najłatwiej zrealizować w metodzie FormCreate() okna głównego (lub dialogu konfiguracyjnego), moja wielokulturowa propozycja jest taka:

Kod: Zaznacz cały

procedure TMain.FormCreate ( Sender : TObject ) ;
var  serialPorts : string;
begin
  {$IFNDEF LINUX}
  {$NOTE windowsowe GetSerialPortNames }
  serialPorts := GetSerialPortNames;
  {$ELSE}
  {$NOTE wlasna implementacja GetSerialPortNamesEx }
  serialPorts := GetSerialPortNamesEx;
  {$ENDIF}                                                                                                                                             
  if serialPorts = '' then with Application do
  begin
     MessageBox( 'Brak wolnych portów szeregowych.','oops!', MB_OK + MB_ICONEXCLAMATION );
     Terminate;
     exit;
  end;
  // tu zawsze choć jeden element, jest bezpiecznie
  SerialPortComboBox.Items.CommaText := serialPorts;   
  SerialPortComboBox.Text := SerialPortComboBox.Items[ 0 ];
  //... dalsza inicjalizacja kontrolek
end;


Dodanie powyższego nie wymaga specjalnych ceregieli, jedynie co to unit LCLType w sekcji uses - w nim są stałe MB_OK i reszta.

Łatka w praktyce:

04_ser_source.png


Jak widać, w przypadku niemożności zaproponowania jakiegokolwiek portu - aplikacja zejdzie, więc takie podejście nadaje się do konfigurowania ad-hoc, a nie przygotowywania ustawień off-line, na sucho. Po drugie - test dostępności polega na otwarciu (i automatycznym zamknięciu ) połączenia, nie zawsze możemy sobie na to pozwolić bez konsekwencji w postaci mignięcia sygnałami sterującymi typu DTR, CTS, etc. No i na koniec jedna sprawa, w sumie to nie wiem czemu tak omijana - komunikaty w oknie Messages środowiska. Jest dyrektywa $NOTE, która umożliwia wypisanie w strumieniu komunikatów swej własnej wiadomości na przykład o ścieżce dla kompilacji warunkowej czy przyjętych do budowania stałych etc, to fajna sprawa - na zrzutce ekranu widać i te meldunki.

#slowanawiatr
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
______________________________________________ ____ ___ __ _ _ _ _
Kończysz tworzyć dopiero, gdy umierasz. (Marina Abramović)

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1259
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: [Linux][Pascal] Lista (naprawdę) dostępnych portów szeregowych

Postautor: gaweł » piątek 18 sty 2019, 00:35

A możesz dołączyć komplet plików do oblukania?
W moim środowisku PC-towym zawsze jest ileś COM-ów (nawet tych prawdziwych jest 6) i rzeczywiście nie zaistnieje sytuacja braku owych (alter alterum docet).

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
tasza
Geek
Geek
Posty: 1082
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

Re: [Linux][Pascal] Lista (naprawdę) dostępnych portów szeregowych

Postautor: tasza » piątek 18 sty 2019, 17:29

Aj sorki, że późno, ale mam Kongo w pracy przed weekendem,
no to źródełka po zmianach (na bazie Terminal.7z) w załączniku, takoż diff.txt i konsolka z kompilacji.
U mnie działa :)
na-linuks-tez-rabotajet.png
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
______________________________________________ ____ ___ __ _ _ _ _
Kończysz tworzyć dopiero, gdy umierasz. (Marina Abramović)

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1259
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: [Linux][Pascal] Lista (naprawdę) dostępnych portów szeregowych

Postautor: gaweł » piątek 18 sty 2019, 20:50

tasza pisze:mam Kongo w pracy przed weekendem,

Mój kolega z uniwerku (z chemii) mawia: "tlenek horroru" albo "kaszanian potasu". :lol:

Dzieje się z takiego powodu:

Kod: Zaznacz cały

procedure TSerwerCfgForm.SendButtonClick ( Sender : TObject ) ;
var
  CommandToSend          : string ;
begin (* TSerwerCfgForm.SendButtonClick *)
  CommandToSend := CfgDataEdit . Text + string ( chr ( $0D ) ) ;
  SerialChannel . WriteData ( CommandToSend . UpperCase ( CommandToSend ) ) ;
end (* TSerwerCfgForm.SendButtonClick *) ;

Dokładniej: CommandToSend.UpperCase
Tak prawdę mówiąc, to jest przeróbka programu SERWERCFG do lutowego numeru EdW. Tam ta funkcjonalność była konieczna, tutaj "przemyciła mi się między palcami". Dzięki, że zwróciłaś mi uwagę na ten szczegół, bo chyba uppercase w tym przypadku nie jest potrzebne. No cóż... zawsze coś... gdzieś... umyka, ale, jak widać, zawsze znajdzie się ktoś, coś, co naprawi co jest zepsute. Uniwersum zawsze znajdzie drogę do celu. Dzięki ci serdeczne.

Program obowiązkowo oblukam.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
j23
Expert
Expert
Posty: 506
Rejestracja: czwartek 08 paź 2015, 18:40

Re: [Linux][Pascal] Lista (naprawdę) dostępnych portów szeregowych

Postautor: j23 » piątek 18 sty 2019, 23:43

Dziękuję za kawałek bardzo przydatnego kodu do Lunuxa. :)
Internet łączy ludzi, którzy dzielą się swoimi zainteresowaniami, pomysłami i potrzebami, bez względu na geograficzne (przeciwności).
BOB TAYLOR, PARC


Wróć do „Inne języki programowania”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 11 gości