Koledzy, czy istnieje jakiś komponent lub inny sposób w Lazarusie, dzięki któremu pobiorę współrzędne z tabletu z wbudowanym odbiornikiem gps i wyświetlę je w aplikacji?
Tablet z Windows 10, 32 bit.
wyszukaj w managerze urządzeń nazwę odbiornika i poszukaj na necie opisu. Możliwe, że producent udostępnia SDK lub jakiś DLL, który uda Ci się zaimplementować jako oleobject w lazarusie
A jaki to jest tablet?
Ten konkretny to Kruger&Matz EDGE 802, aczkolwiek szukam oczywiście jakiejś uniwersalnej metody na każdy tablet z Windowsem i wbudowanym GPSem.
@axel234: nie wiem jak w Twoim wypadku ale ja jakiś czas temu (również tu na forum) bawiłem się z tabletem i obsługą diody led. Okazało się bowiem, że Microsoft przygotował funkcję w WinAPI do tego. Jeśli Twój tablet ma zainstalowaną jakąś aplikację do obsługi GPS to spróbuj podejrzeć processexplorerem, co faktycznie się dzieje przy odczycie współrzędnych. Możliwe, że również jest jakaś funkcja, którą wystarczy wywołać ;)
Jest też inne (nowsze) API (Geolocation) do tego, ale tylko w aplikacjach UWP - zasadniczo niedostępne dla aplikacji Win32, chyba, że tworzysz aplikację za pomocą "Project Centennial". Główny problem polega na tym, że po prostu te moduły GPS wbudowane w tablety często prezentują dane tylko przez ILocation/Geolocation API, nie ujawniajac np. surowych danych NMEA, które by były zrozumiałe dla aplikacji zewnętrznych.
Alternatywnie: GPSReverse (https://www.turboirc.com/gps7/) jest w stanie wystawić dane z ILocation jako port szeregowy.
Odbiornik pewnie standardowo udostępnia dane ciągiem NMEA wysyłanym po wirtualnym porcie szeregowym :-)
Dzięki za podpowiedzi. Będę kombinował.
Wiem, że chodzi o Lazarus ale gdyby nic z tego nie wyszło może lepiej byłoby to zrobić w Delphi tam jest komponent TLocaionSensor
(mam nadzieję że nie "wycięty" w darmowym Starter).
Tutaj ktoś w komentarzach wkleił przykład użycia ILocation
w C++.
Wystarczy przerobić na Delphi.
Azarien napisał(a):
Tutaj ktoś w komentarzach wkleił przykład użycia
ILocation
w C++.
Wystarczy przerobić na Delphi.
A w przypadku interfejsów oznacza to alb samodzielną ich implementację albo import LocationApi 1.0 Type Library (przynajmniej 1.0 jest w Windows 7 a w 10 może być nowsza wersja choć nie musi). Ponieważ w Lazarus chyba nie ma (przynajmniej kiedyś nie było nie wiem jak obecnie) możliwości importu podaję wygenerowany przez Delphi kod może się przyda (choć nie ma pewności, że nie będzie wymagał przeróbek pod Lazarus)
https://4programmers.net/Pastebin/6448
Dzięki koledzy za dyskusję i podpowiedzi.
Udało mi się uruchomić moduł od @kAzek zarówno w Delphi jak i - po poprawkach - w Lazarusie. Jeszcze nie ogarnąłem rzutowania, ale doczytam jak to zrobić.
Niestety w między czasie tablet z GPSem już odjechał, bo miałem go tylko na kilka dni. W związku z tym mam pytanie, czy można jakoś zasymulować GPS, tak żeby ten moduł uznał, że odbiornik jest wbudowany w komputerze?
Jeszcze nie ogarnąłem rzutowania, ale doczytam jak to zrobić.
Po prostu rzutuj parametr na WideString
, np.:
class function CoDispLatLongReport.CreateRemote(const MachineName: string): IDispLatLongReport;
begin
Result := CreateRemoteComObject(WideString(MachineName), CLASS_DispLatLongReport) as IDispLatLongReport;
end;
Niestety w między czasie tablet z GPSem już odjechał, bo miałem go tylko na kilka dni. W związku z tym mam pytanie, czy można jakoś zasymulować GPS, tak żeby ten moduł uznał, że odbiornik jest wbudowany w komputerze?
Znowu to, do czego dawałem link jako anonim - GPSDirect. https://www.turboirc.com/gps7/ Ma możliwość symulacji urządzenia GPS. W wersji trial to jest chyba nawet za darmo na zawsze o ile dobrze pamiętam.
Za dobrze by było gdyby wszystko poszło jak po maśle.
Poszperałem trochę w sieci w poszukiwaniu info o LocationApiLip i znalazłem kod wyświetlający współrzędne w Editach.
Kod w Lazarusie się kompiluje, ale po wciśnięciu Buttona otrzymuję komunikat No devices detected!.
Próbowałem też z zainstalowaną i uruchomioną symulacją GPSa poleconą przez @Ktos
Gdzie dałem ciała, lub co pominąłem?
function IsLocationAPIAvailable: Boolean;
var
loc: ILocation;
begin
Result := False;
try
loc := CreateComObject(CLASS_Location) as ILocation;
Result := True;
except
end;
loc := nil;
end;
function LocReportStatus(status: LOCATION_REPORT_STATUS): String;
begin
Result := '';
case status of
REPORT_RUNNING:
Result := 'Report received!';
REPORT_NOT_SUPPORTED:
Result := 'No devices detected!';
REPORT_ERROR:
Result := 'Report error!';
REPORT_ACCESS_DENIED:
Result := 'Access denied!';
REPORT_INITIALIZING:
Result := 'Report is initializing!';
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
type
PILocationReport = ^ILocationReport;
var
loc: ILocation;
rep: ILocationReport;
LatLongRep: ILatLongReport;
status: LOCATION_REPORT_STATUS;
hres: HRESULT;
fLat, fLon: double;
begin
if IsLocationAPIAvailable then
begin
loc := CreateComObject(CLASS_Location) as ILocation;
try
status := REPORT_NOT_SUPPORTED;
//have permission?
// 0 for asynchronous calls
if loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True)) = S_OK then
begin
hres := loc.GetReportStatus(IID_ILatLongReport, status);
if hres = S_OK then
begin
if status = REPORT_RUNNING then //report is ok?
begin
hres := loc.GetReport(IID_ILatLongReport, rep);
if (hres = S_OK) and (rep<>nil) then
begin
hres := rep.QueryInterface(IID_ILatLongReport, LatLongRep);
if (hres = S_OK) and (LatLongRep<>nil) then
begin
fLat := 0;
fLon := 0;
LatLongRep.GetLatitude(fLat);
LatLongRep.GetLongitude(fLon);
Edit1.Text := FloatToStr(fLat);
Edit2.Text := FloatToStr(fLon);
end;
end;
end
else
ShowMessage(LocReportStatus(status));
end
else
ShowMessage(IntToStr(Integer(hres)));
end;
finally
LatLongRep := nil;
rep := nil;
loc := nil;
end;
end;
end;
O ile wiem to Lazarus ma debugger więc zawsze można wykonać program krok po kroku i sprawdzić co poszło nie tak.
- Podstawowe pytanie czy w ogóle pokazał się monit o włączenie urządzenia?
- W jakim trybie pracujesz tzn. czy łączysz się przez Wi-Fi ze smartfonem lub iphone posiadającym GPS czy Simulation (bez fizycznego urządzenia ale darmowa licencja)?
Jeżeli masz fizyczne urządzenie to to musi ono błyskawicznie "łapać" sygnał GPS bo w kodzie nie ma żadnego oczekiwania (na stronie z której brałeś ten kod poprawka jest niżej).
Ad 1. Po kliknięciu buttona pojawiło się jedynie info, że ta opcja wymaga włączonych usług lokalizacyjnych i czy chcę je teraz włączyć.
Ad 2. Próbowałem zarówno opcji Simulation jak i z połączonym (prawidłowo) smartfonem.
Wywołanie ILocation Query w GpsComplete daje odpowiedź "No ILocation Available".
To (najlepiej na początek na Simulation) użyj debuggera i zobacz na czym się wywala pewnie na tym
if loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True)) = S_OK then
bo to wywołanie się nie powiedzie jeżeli zgoda już została udzielona (musiałbyś za każdym razem odhaczać zgodę).
Jeżeli masz włączoną zgodę chyba można tymczasowo to wykomentować i sprawdzać co się dalej stanie powinno zwrócić to co wywołanie ILocation Query z menu Tests GPSComplete.
Wykomentowałem.
Wywala się na tej linii:
hres := rep.QueryInterface(IID_ILatLongReport, LatLongRep);
Jak uruchomię wykomentowaną aplikację, dostaję komunikat:
Project raised exception class 'External: SIGSEGV'.
U mnie działa taki kod:
function IsLocationAPIAvailable: Boolean;
var
loc: ILocation;
begin
Result := False;
try
loc := CreateComObject(CLASS_Location) as ILocation;
Result := True;
except
end;
loc := nil;
end;
function LocReportStatus(status: LOCATION_REPORT_STATUS): String;
begin
Result := '';
case status of
REPORT_RUNNING:
Result := 'Report received!';
REPORT_NOT_SUPPORTED:
Result := 'No devices detected!';
REPORT_ERROR:
Result := 'Report error!';
REPORT_ACCESS_DENIED:
Result := 'Access denied!';
REPORT_INITIALIZING:
Result := 'Report is initializing!';
end;
end;
function WaitForReport(loc: ILocation; reportType: TGUID): Boolean;
var
status: LOCATION_REPORT_STATUS;
begin
Result := False;
while (loc.GetReportStatus(reportType, status) = S_OK) do
begin
if status = REPORT_RUNNING then //report ready!
begin
Result := True;
Exit;
end;
if status <> REPORT_INITIALIZING then //wait for report!
begin
//error occured
Exit;
end;
Sleep(200);
end;
end;
procedure TForm1.Button3Click(Sender: TObject);
type
PILocationReport = ^ILocationReport;
var
loc: ILocation;
rep: ILocationReport;
LatLongRep: ILatLongReport;
status: LOCATION_REPORT_STATUS;
hres: HRESULT;
fLat, fLon: double;
begin
if IsLocationAPIAvailable then
begin
loc := CreateComObject(CLASS_Location) as ILocation;
try
status := REPORT_NOT_SUPPORTED;
//have permission?
// 0 for asynchronous calls
//if loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True)) = S_OK then
loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True));
if True then //tak na chwile zamiast sprawdzac czy RequestPermissions = S_OK
begin
if WaitForReport(loc, IID_ILatLongReport) then
begin
hres := loc.GetReportStatus(IID_ILatLongReport, status);
if hres = S_OK then
begin
if status = REPORT_RUNNING then //report is ok?
begin
hres := loc.GetReport(IID_ILatLongReport, rep);
if (hres = S_OK) and (rep<>nil) then
begin
hres := rep.QueryInterface(IID_ILatLongReport, LatLongRep);
if (hres = S_OK) and (LatLongRep<>nil) then
begin
fLat := 0;
fLon := 0;
LatLongRep.GetLatitude(fLat);
LatLongRep.GetLongitude(fLon);
Edit1.Text := FloatToStr(fLat);
Edit2.Text := FloatToStr(fLon);
end;
end;
end
else
ShowMessage(LocReportStatus(status));
end
else
ShowMessage(IntToStr(Integer(hres)));
end;
end;
finally
LatLongRep := nil;
rep := nil;
loc := nil;
end;
end;
end;
initialization
CoInitializeEx(nil, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);
finalization
CoUninitialize;
No i oczywiście nie działa. Cały czas sprawdzałem to pod Delphi 2007.
Ten warunek
if WaitForReport(loc, IID_ILatLongReport) then
nie jest spełniany. Bez niego pojawia się: No devices found.
Przed chwilą wrzuciłem ten kod do Lazarusa i pojawił się błąd w liniach:
initialization
CoInitializeEx(nil, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);
finalization
CoUninitialize;
(no idetifier found).
Czy w Lazarusie powinienem coś dodatkowo zadeklarować? W Delphi przechodzi, ale zależy mi żeby docelowo korzystać z tego kodu w Lazarusie.
EDIT: W Lazarusie, do uses dodałem ActiveX, to już błąd w initialization nie występuje, ale za to pojawia się teraz w tej linii:
if loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True)) = S_OK then
A komunikat to:
unit1.pas(108,55) Error: Call by var for arg no. 1 has to match exactly: Got "ACTIVEX._RemotableHandle" expected "LOCATIONAPILIB._RemotableHandle"
Tak u mnie wygląda ekran GpsComplete.
Chyba znalazłem odpowiedź:
https://naughter.wordpress.com/2015/05/24/changes-in-the-windows-10-sdk-compared-to-windows-8-1-part-two/
A konkretnie mam na myśli fragment ze zrzutu w załączniku.
axel234 napisał(a):
Chyba znalazłem odpowiedź:
https://naughter.wordpress.com/2015/05/24/changes-in-the-windows-10-sdk-compared-to-windows-8-1-part-two/A konkretnie mam na myśli fragment ze zrzutu w załączniku.
Raczej bez znaczenia, w WinAPI jest mnóstwo rzeczy które są “deprecated” a wciąż działają.
Np. takie DirectDraw jest “deprecated” od 17 lat, a używam go z powodzeniem w Windows 10.
Gdyby ILocation
miało przestać działać, to już CoCreateInstance
by zwracało błąd.
Ja tu podejrzewam grubszą sprawę typu brak uprawnień jak wiadomo w nowszych systemach nawet do założenia niektórych hooków potrzeba nie wiadomo czego nawet podpisu cyfrowego aplikacji być może w 10 takie jest wymaganie odnośnie dostępu do lokalizacji ale to by było trochę głupie. Najbardziej zastanawia mnie fakt że w przypadku GpsComplete system raz zapytał o zgodę na dostęp a w testowym programie nie ma zamiaru (na win 7 pyta jeżeli takiej zgody jeszcze nie ma).
GpsComplete ma podpis cyfrowy i być może dlatego działa (choć równie dobrze ten podpis potrzebny jest tylko do instalacji sterowników ale może do tego i do tego).