Integracja z DUST Go
DUST Go to wbudowana przeglądarka mobilna: wczytuje aplikację internetową w komponencie WebView i udostępnia stronie sprzęt urządzenia do skanowania DUST za pośrednictwem niewielkiego mostu JavaScript, @dustid/dust-go-connect. Tworzysz i hostujesz zwykłą aplikację internetową, a DUST Go zapewnia kamerę, akcesoria optyczne i potok wykonywania ujęć — bez potrzeby korzystania z natywnych narzędzi programistycznych.
Ta strona prowadzi do jednego rozpoznanego skanu. Wszystko, co wykracza poza ten etap — logowanie, geolokalizację, zgodność i sprzęt na komputery stacjonarne — opisano w sekcji Poza pierwszym skanem poniżej.
Zanim zaczniesz
- Rola
- Poświadczenie konta usługi przechowywane przez backend
- Urządzenie
- iPhone z aplikacją DUST Go i obsługiwanym akcesorium Loupe do wykonywania ujęć DUST
Jak to wszystko się łączy
Dział zatytułowany „Jak to wszystko się łączy”- Użytkownik otwiera aplikację internetową w DUST Go (za pomocą linku aplikacji).
- Strona importuje
@dustid/dust-go-connect; biblioteka wykrywa most DUST Go i udostępnia obiektconnector. - Strona wywołuje
scanAsync(). DUST Go otwiera natywny skaner nad stroną. - Po wykonaniu ujęcia DUST Go przekazuje stronie zdarzenie skanowania: ładunek zawiera dane i metadane ujęcia.
- Strona wysyła ujęcie do twojego backendu, który wywołuje APID (
/api/v1/tags/identify,/bindlub/verify), aby je rozpoznać.
Skan DUST przekazuje stronie surowe ujęcie (obraz JPEG zakodowany w base64) wraz z metadanymi ujęcia — a nie rozpoznany identyfikator. Identyfikacja, weryfikacja i powiązanie odbywają się w całości po stronie serwera.
Instalacja
Dział zatytułowany „Instalacja”npm install @dustid/dust-go-connectPakiet nie ma żadnych zależności, jest udostępniany na licencji MIT i zawiera definicje typów TypeScript.
Wykrywanie DUST Go
Dział zatytułowany „Wykrywanie DUST Go”Eksport connector ma wartość undefined, gdy strona nie działa w DUST Go, więc ta sama kompilacja aplikacji może obsługiwać zarówno zwykłe przeglądarki, jak i DUST Go:
import { connector } from "@dustid/dust-go-connect";
export const insideDustGo = Boolean(connector);Warto pamiętać o dwóch rzeczach:
- Moment importu. Wykrywanie następuje w chwili importu modułu, w przeglądarce. Jeśli korzystasz z renderowania po stronie serwera, uzależnij każde użycie obiektu connector od zakończenia hydratacji.
- Wykrywanie po stronie serwera. Nowsze kompilacje DUST Go dodają też do nagłówka User-Agent komponentu WebView oznaczenie
DustGo/<version> (<app id>), dzięki czemu serwer może wykryć aplikację, zanim zostanie uruchomiony jakikolwiek kod JavaScript. Traktuj to jako wskazówkę, a nie granicę bezpieczeństwa — każdy może sfałszować nagłówek User-Agent.
Wykonywanie skanu
Dział zatytułowany „Wykonywanie skanu”Najprostszą drogą jest API oparte na obietnicach (promise) — wyświetl skaner i zaczekaj na jedno ujęcie:
import { scanAsync } from "@dustid/dust-go-connect";
const payload = await scanAsync();// payload: { type: 'DUST' | 'QR' | 'BARCODE' | 'DATA_MATRIX' | 'NFC',// data: string, metadata?: ScanMetadata }scanAsync() zwraca Promise<ScanPayload>. Obietnica zostaje odrzucona, jeśli użytkownik zamknie skaner bez wykonania ujęcia, a także natychmiast po wywołaniu poza DUST Go (gdy obiekt connector nie istnieje) — dlatego najpierw sprawdź connector, jeśli ta sama ścieżka kodu działa w zwykłych przeglądarkach.
Zawartość ładunku
Dział zatytułowany „Zawartość ładunku”payload.type | payload.data | Uwagi |
|---|---|---|
DUST | Obraz JPEG ujęcia DUST zakodowany w base64 | Duży; rozpoznaj go po stronie serwera za pomocą APID |
QR, BARCODE, DATA_MATRIX | Zdekodowana zawartość symbolu | |
NFC | Identyfikator chipu NFC (UID): wielkie litery szesnastkowe bez separatorów, np. 04A1B2C3D4E5F6 | Taki sam na iPhonie i na Androidzie |
payload.metadata (typ ScanMetadata) opisuje ujęcie: identyfikatory urządzenia (deviceId, modelName, osName, osVersion, appVersion), wybór obiektywu i kamery oraz opcjonalnie pola optyki (zoom, ostrość, ekspozycja, ISO), geolokalizację (latitude/longitude/accuracy) i szczegóły źródła ujęcia w przypadku zewnętrznych akcesoriów skanujących (captureSource, usbVendorId, usbProductId, dragonBackend). Podczas powiązania przekazuj je bez zmian — APID zapisuje je razem z identyfikatorem.
Gdzie przechowywane są poświadczenia
Dział zatytułowany „Gdzie przechowywane są poświadczenia”Przeczytaj tę sekcję przed krokiem rozpoznawania: od niej zależy kształt całej integracji.
Dlatego krok rozpoznawania to dwa pliki w dwóch miejscach:
| Plik | Gdzie działa | Czy przechowuje poświadczenie DUST? |
|---|---|---|
| Strona skanowania | Przeglądarka w DUST Go | Nie |
Procedura obsługi /api/dust-scan | Twój serwer | Tak |
Rozpoznawanie skanu w APID
Dział zatytułowany „Rozpoznawanie skanu w APID”Ujęcie DUST staje się przydatne dopiero wtedy, gdy APID je dopasuje. Strona dekoduje ujęcie z base64 do postaci binarnej i wysyła je do własnego punktu końcowego:
// No DUST credential in this file.async function identifyDustScan(base64Jpeg: string) { // The scan arrives base64-encoded; APID expects binary multipart data. const bytes = Uint8Array.from(atob(base64Jpeg), (c) => c.charCodeAt(0)); const form = new FormData(); form.set("operation", "identify"); form.set("tagType", "DUST"); form.set("data", new Blob([bytes], { type: "image/jpeg" }));
const response = await fetch("/api/dust-scan", { method: "POST", body: form, credentials: "same-origin", }); return { status: response.status, body: await response.json() };}Backend dodaje poświadczenie i kontekst oraz sam określa zakres skanowania:
export async function handleIdentify(form: FormData, session: Session) { // `searchTeamIds` is a JSON array of Team UUIDs. Set it on the server — a // browser-supplied scope is a request, never an authorization. form.set("searchTeamIds", JSON.stringify(session.allowedTeamIds));
const response = await fetch("https://apid.dustid.io/api/v1/tags/identify", { method: "POST", headers: { Authorization: `Bearer ${await getDustToken()}`, "Dust-Ctx-Org-Id": session.organizationId, }, body: form, });
// Pass the status and body through unchanged: the page needs to tell // "nothing matched" (404 IDENTIFIER_NOT_FOUND) from "try again" // (503 SCAN_SEARCH_INCOMPLETE). return new Response(await response.text(), { status: response.status, headers: { "Content-Type": "application/json" }, });}Pole nazywa się searchTeamIds. Ładunki identyfikacji odrzucają właściwości, których nie deklarują, więc starsza pisownia searchGroupIds powoduje niepowodzenie całego żądania z kodem 400 INVALID_REQUEST, zamiast zostać zignorowana. Jedyną zachowaną starszą nazwą z group jest nagłówek Dust-Ctx-Grp-Id, nadal akceptowany jako alias Dust-Ctx-Team-Id.
Ten sam kształt multipart obsługuje dwie pozostałe operacje:
/api/v1/tags/bind— dodajthreadId. Poleoptions.enrollmentSessionIdjest opcjonalne: podaj UUID wygenerowany po stronie klienta i używany ponownie w ramach całej serii, gdy kilka ujęć należy do siebie (stanowisko rejestracji przetwarzające partię); pomiń je przy jednorazowym powiązaniu./api/v1/tags/verify— dodajthreadIditags, czyli tablicę obiektów ([{ "tagId": "…", "tagType": "DUST" }], zakodowaną jako JSON w multipart), a nie ciągów z identyfikatorami. Poletagsjest wymagane.
Pełne kontrakty żądań i odpowiedzi opisano w sekcji Identyfikatory, a w sekcji Skaner React znajduje się gotowy do skopiowania komponent, który implementuje wszystkie trzy operacje względem procedury obsługi w backendzie, takiej jak powyższa.
Testowanie integracji
Dział zatytułowany „Testowanie integracji”- Zainstaluj DUST Go z App Store lub Google Play (wymagania sprzętowe opisano w sekcji Obsługiwane urządzenia — ujęcie DUST wymaga obsługiwanego akcesorium optycznego).
- Udostępnij aplikację przez HTTPS pod adresem URL osiągalnym z urządzenia (podczas programowania wystarczy adres w sieci LAN).
- W DUST Go dodaj swój adres URL jako niestandardowy link aplikacji i otwórz go.
- Upewnij się, że obiekt connector został wykryty, a następnie wykonaj skan.
- Potwierdź, że ujęcie dociera do backendu, a wywołanie DUST wykonywane przez backend zwraca wynik, który możesz wyświetlić — również w przypadku „brak dopasowania”.
Aby zobaczyć działanie całego mostu (wykrywanie, scanAsync, możliwości, dziennik zdarzeń), otwórz w DUST Go demonstrację DUST Go.
Poza pierwszym skanem
Dział zatytułowany „Poza pierwszym skanem”Wszystko poniżej jest opcjonalne. Wróć do tej części, gdy pojedynczy skan będzie rozpoznawany od początku do końca.
Przepływy pracy z wieloma skanami
Dział zatytułowany „Przepływy pracy z wieloma skanami”W przepływach typu „zeskanuj wiele, a potem wyślij” użyj API nasłuchiwania — skaner pozostaje otwarty między kolejnymi ujęciami:
import { connector } from "@dustid/dust-go-connect";
connector?.add("my-listener", (event) => { switch (event.type) { case "scan": handleScan(event.payload); break; case "hide": // scanner closed case "show": // scanner opened break; default: // Ignore unknown event types — the protocol may grow. break; }});
connector?.showScanner();// later: connector?.hideScanner(); connector?.remove("my-listener");addScanListener działa tak samo, ale bez zarządzania nasłuchiwaczami — otrzymuje
każdy skan i zwraca własną funkcję anulowania subskrypcji:
import { addScanListener } from "@dustid/dust-go-connect";
const stop = addScanListener((payload) => handleScan(payload));// later: stop();Jedna sesja skanera może dostarczyć serię ujęć, dlatego wysyłaj je pojedynczo. Ładunek DUST to duży obraz JPEG w base64; wysyłanie kilku naraz przeciąża połączenie i nie daje operatorowi użytecznej informacji o postępie. Umieszczaj ładunki w kolejce i czekaj na zakończenie każdego wysyłania przed rozpoczęciem następnego.
Możliwości skanera
Dział zatytułowany „Możliwości skanera”Hosty się różnią — telefon może regulować zoom i ekspozycję, akcesorium w postaci mikroskopu USB udostępnia inny zestaw ustawień, a starsza kompilacja może nie udostępniać niczego. Zapytaj o możliwości, zamiast wnioskować o nich z nagłówka User-Agent:
const capabilities = connector?.getCapabilities?.();Brak ogłoszenia lub puste ogłoszenie traktuj jako tylko pojedyncze ujęcia, bez regulowanych ustawień. Nigdy nie traktuj braku odpowiedzi jako dostępności danej możliwości.
Geolokalizacja
Dział zatytułowany „Geolokalizacja”Standardowe wywołania navigator.geolocation działają w DUST Go — aplikacja w sposób przezroczysty przekazuje je przez natywny monit systemu operacyjnego o uprawnienia. Nie jest potrzebny żaden kod korzystający z obiektu connector.
Przepływy logowania w DUST Go
Dział zatytułowany „Przepływy logowania w DUST Go”Jeśli aplikacja korzysta z OAuth/OIDC, pamiętaj, że DUST Go przekazuje nawigację do zewnętrznych dostawców tożsamości do przeglądarki systemowej, a wywołanie zwrotne wraca do strony przez niestandardowy schemat URL.
Gdy tworzysz na stronie parametr OAuth redirect_uri, zawsze go opakowuj:
import { connector } from "@dustid/dust-go-connect";
const origin = window.location.origin;const redirectUri = ( connector?.rewriteRedirect(new URL(`${origin}/auth/callback`)) ?? new URL(`${origin}/auth/callback`)).href;Poza DUST Go (oraz w hostach, w których przepisanie nie jest potrzebne) ten kod niczego nie zmienia. W DUST Go zamienia protokół adresu URL na niestandardowy schemat aplikacji, tworząc URI w rodzaju com.dustidentity.dustgo://your-host/auth/callback (dokładny schemat pochodzi z kompilacji DUST Go; wartością zapasową jest dustgo), który dostawca tożsamości musi mieć zarejestrowany jako dozwolony URI przekierowania.
Wersjonowanie i zgodność
Dział zatytułowany „Wersjonowanie i zgodność”Most korzysta z numerowanego protokołu. Strona ogłasza, które zdarzenia rozumie, w automatycznym uzgadnianiu hello w chwili importu, a host wysyła wyłącznie zdarzenia zadeklarowane przez stronę — dzięki temu starszy host i nowsza strona współpracują z ograniczoną funkcjonalnością, zamiast przestać działać.
Numer odczytaj z zainstalowanego pakietu, a nie z tej strony: SDK go eksportuje.
import { CONNECT_PROTOCOL_VERSION } from "@dustid/dust-go-connect";| Protokół | Co dodano |
|---|---|
| 1 | Niejawny starszy kontrakt: scan / hide / show, bez uzgadniania. |
| 2 | Uzgadnianie hello i zdarzenie calibrationResult. |
| 3 | Ogłoszenie capabilities wysyłane przez host do strony, polecenie runCapture wysyłane przez stronę do hosta oraz odpowiadające na nie zdarzenie captureStatus. |
| 4 | CaptureRequirements.app (wymuszana przez host zasada wersji aplikacji) i CaptureCapabilities.enforcedRequirements, dzięki czemu strona może odróżnić host, który egzekwuje wymaganie, od takiego, który by je zignorował. |
| 5 | ScanPayload.exif — fotograficzne dane EXIF ujęcia wraz z informacją o ich pochodzeniu. Wyłącznie rozszerzenie, bez zmian w istniejącym działaniu. |
| 6 | Polecenie capturePhoto wysyłane przez stronę do hosta i odpowiadające na nie zdarzenie photo: zwykłe zdjęcie (na przykład załącznik rekordu), celowo niebędące skanem. |
SDK w drzewie źródłowym tej dokumentacji to @dustid/dust-go-connect 0.2.0, który obsługuje protokół 6. Ta strona nie określa, co obecnie oferuje publiczny rejestr npm — sprawdź CONNECT_PROTOCOL_VERSION i pole version faktycznie zainstalowanego pakietu, a jeśli potrzebujesz konkretnego wydania, zwróć się do DUST Identity.
Zasady, które obowiązują niezależnie od numeru wersji:
- Rozstrzygające jest wykrywanie możliwości w czasie działania. Numer protokołu określa, co potrafi wyrazić SDK strony; nic nie mówi o hoście po drugiej stronie ani o podłączonym sprzęcie. Zapytaj za pomocą
getCapabilities()i traktuj brak ogłoszenia jako „tylko pojedyncze ujęcia, bez regulowanych ustawień”. - Rozgałęziaj kod według
event.typei ignoruj to, czego nie rozpoznajesz. Dochodzą nowe typy zdarzeń; strona, która zgłasza wyjątek przy nieznanym typie, przestanie działać po aktualizacji hosta, która w ogóle nie powinna jej dotyczyć. - Ręczna kontrolka w interfejsie hosta nie jest możliwością protokołu, a pomyślnie wykonane polecenie sprzętowe nie dowodzi, że wartość została zastosowana w chwili wykonania ujęcia.
- Zdarzenia
calibrationResultiackCalibrationResults()to wewnętrzne mechanizmy własnych przepływów kalibracji DUST — integracje zewnętrzne mogą je ignorować.
Sterowanie urządzeniem Dragon na komputerze
Dział zatytułowany „Sterowanie urządzeniem Dragon na komputerze”Ta sekcja dotyczy urządzenia Dragon podłączonego do komputera stacjonarnego
i sterowanego ze zwykłej przeglądarki za pośrednictwem aplikacji towarzyszącej.
Nie jest to ścieżka dla Androida: w DUST Go na Androidzie aplikacja sama steruje
podłączonym urządzeniem Dragon, a jego ujęcia trafiają do strony przez zwykły
przepływ runCapture opisany powyżej — createDragonClient() nie bierze w tym
udziału, a na telefonie nie instaluje się żadnej aplikacji towarzyszącej. Zobacz
Obsługiwane urządzenia.
Witryna może przejąć podgląd z urządzenia Dragon i sterowanie jego ostrością. Zainstalowana aplikacja towarzysząca obsługuje polecenia USB w tle. Przy pierwszym użyciu użytkownicy zatwierdzają witrynę w DUST Camera Controls i, gdy pojawi się monit, przyznają przeglądarce dostęp do kamery i do urządzeń lokalnych. Zatwierdzenie jest zapamiętywane dla danej witryny i przeglądarki. Witryna musi korzystać z HTTPS. Camera Controls może pozostawać na pasku menu z zamkniętym oknem; żadne wyskakujące okno połączenia nie jest wymagane. Nie jest też wymagana żadna witryna hostowana przez DUST.
Zainstaluj DUST Camera Controls w folderze Aplikacje i uruchom ją raz. Aplikacja pokazuje, czy urządzenie Dragon jest podłączone, i pozostaje dostępna na pasku menu. Uruchamianie przy logowaniu jest domyślnie włączone; możesz je wyłączyć w aplikacji.
Instalatory dla poszczególnych platform są wymienione na stronie Pliki do pobrania. Dystrybucje z obsługą aktualizacji automatycznie wyszukują aktualizacje i udostępniają polecenie Wyszukaj aktualizacje… na pasku menu. To ty decydujesz, kiedy zainstalować aktualizację; przed ponownym uruchomieniem aplikacji zapisz swoją pracę i zakończ skanowanie. Wersje ewaluacyjne bez kanału aktualizacji wymagają zastępczego instalatora od DUST.
W DICE otwórz Skanowanie, wybierz DUST, włącz Sterowanie ostrością Dragon pod skanerem i przy pierwszym użyciu wybierz Połącz. Po zatwierdzeniu obszar podglądu udostępnia autofokus, ustawienia ostrości oraz przycisk Zeskanuj, który wykonuje wybraną operację. Gdy wrócisz do skanowania z włączonym trybem, DICE połączy się automatycznie, jeśli Camera Controls działa, a dostęp przeglądarki do kamery jest nadal przyznany. Jeśli uprawnienia przeglądarki wymagają uwagi, wybierz Połącz lub Uruchom kamerę, aby dokończyć łączenie.
import { createDragonClient } from "@dustid/dust-go-connect";
const dragon = createDragonClient();const video = document.querySelector("video")!;
// First use: request native approval and browser permissions from a user action.connectButton.onclick = async () => { try { await dragon.connect(); await dragon.openVideo(video); const controls = await dragon.getControls(); const focus = controls.find((control) => control.name === "focus"); // Use focus.min / focus.max for your manual slider when focus is available. } catch (error) { showConnectionError(error); }};
// On a later visit, reuse approval without creating a native approval prompt.// If this rejects, present Connect Dragon; do not repeatedly request approval.// await dragon.connect({ interactive: false });// Reopen video only after browser camera permission has already been granted.
manualSlider.onchange = async () => { await dragon.setControl("focus", Number(manualSlider.value));};
autofocusButton.onclick = async () => { try { const result = await dragon.autofocus(video, { radius: Number(autofocusRangeSlider.value), onProgress: ({ position, sampled }) => showProgress(position, sampled), }); manualSlider.value = String(result.position); // "low-contrast" means autofocus retained the starting focus. showFocusResult(result.outcome); } catch (error) { showFocusError(error); }};cancelButton.onclick = () => dragon.cancelAutofocus();
captureButton.onclick = async () => { const payload = await dragon.capture(video); // Send the raw DUST JPEG to your server, which calls the Identifier API. await sendToYourServer(payload);};
const unsubscribe = dragon.onState(({ connected, controls }) => { updateDeviceUI(connected, controls);});// On page/component teardown: unsubscribe(); dragon.dispose();W danej chwili urządzeniem Dragon może sterować tylko jedna sesja witryny. Użytkownik może cofnąć dostęp za pomocą polecenia Zarządzaj dostępem witryn… w Camera Controls. Zamknięcie natywnego okna pozostawia połączenie dostępne; zamknięcie aplikacji je przerywa. Zatwierdzona sesja otrzymuje aktualizacje stanu urządzenia, gdy urządzenie Dragon zostanie odłączone lub ponownie podłączone. Po ponownym podłączeniu otwórz wideo jeszcze raz. Podłączenie sprzętu nie daje witrynom dostępu do niego bez wiedzy użytkownika.
Autofokus przeszukuje zakres wokół ostatniego polecenia ostrości, ograniczony skalibrowanym zakresem urządzenia. Suwak zakresu określa odległość po każdej stronie tej pozycji początkowej. Pozostaw stronę widoczną, a próbkę nieruchomą, z interesującym szczegółem na środku obrazu. Próbka pozbawiona wyraźnych cech lub słabo oświetlona może nie zapewniać kontrastu wystarczającego do ustawienia ostrości. Przed wykonaniem ujęcia obejrzyj uzyskany obraz.
Wartości ostrości są poleceniami, a nie zmierzonymi pozycjami obiektywu. Te
metody ręcznego sterowania nie gwarantują ustawień w chwili wykonania zdjęcia i
nie umożliwiają ujęć zgodnych z planem ani skanów scalonych. Skaner mobilny i
jego dotychczasowy kontrakt scanAsync() pozostają dostępne niezależnie.
Powiązane materiały
Dział zatytułowany „Powiązane materiały”- Identyfikatory — API, względem którego ta integracja rozpoznaje ujęcia.
- Błędy i wyniki skanowania — tabele wyników, od których musi zależeć działanie interfejsu skanowania.
- Obsługiwane urządzenia — sprzęt wymagany do wykonywania ujęć DUST.
- Skaner React — ten sam przepływ bez DUST Go, z użyciem kamery urządzenia.
- Tworzenie z agentami AI — umiejętność agenta obejmująca tę integrację, przeznaczona dla agentów programistycznych.
Kamery Dragon z obsługą AMR raportują skalibrowane powiększenie optyczne obok ostrości w DICE i DUST Camera Controls. Subskrybuj onState, aby otrzymywać state.magnification, lub wywołaj await dragon.getMagnification(), aby uzyskać bieżący odczyt. Opcjonalny odczyt zawiera value, unit: "x" i raw (pozycję czujnika w ćwiartkach jednostki). Starsze aplikacje towarzyszące, nieobsługiwana kalibracja i nieudane odpytania nie dają odczytu; nigdy nie zastępuj go zerem. Powiększenie jest tylko do odczytu i jest niezależne od ostatniego polecenia ostrości wydanego silnikowi.