ट्रेन छूटने से बचने के लिए एक Rust प्रोजेक्ट
(lilymara.xyz)- सैन फ्रांसिस्को में बिना कार के आने-जाने वाली जीवनशैली में, घर से निकलने से पहले पास के स्टॉप पर अगली arrival time तुरंत दिखाने वाला डिवाइस ज़्यादा उपयोगी है
- एक पुराने Kindle को jailbreak करके दीवार पर लगने वाला e-ink डिस्प्ले बनाया गया, और शुरुआत में BART वेबपेज के screenshot इमेज समय-समय पर दिखाए जाते थे
- MUNI जोड़ने पर कई browser tabs, Raspberry Pi की memory पर दबाव, और HTTP 429 जैसी समस्याओं के कारण screenshot-आधारित संरचना की विश्वसनीयता घट गई
- Rust में दोबारा लिखे गए संस्करण में 511.org Stop Monitoring API से डेटा लाया जाता है, और
skia-safeसे Kindle resolution और 8-bit color depth के अनुरूप PNG सीधे बनाया जाता है - browser engine हटाकर caching, error display, और layout adjustment जोड़ने के बाद यह एक व्यावहारिक Kindle public transit display बन गया
घर से निकलते समय ज़रूरी जानकारी सरल होती है
- सैन फ्रांसिस्को में बिना कार के यात्रा करते हुए बस, ट्रेन, ट्राम, ट्रॉलीबस, केबल कार और फेरी जैसे public transit का अक्सर उपयोग किया जाता है
- CityMapper जैसे ऐप गंतव्य तक पहुँचने के लिए उपयोगी हैं, लेकिन घर के सामने “कहाँ जाना है” से ज़्यादा हर route की अगली arrival time महत्वपूर्ण होती है
- अगर उपयोगकर्ता पहले से जानता है कि कौन-सा स्टॉप और route लेना है, तो सामान्य navigation ऐप का current location और destination मॉडल ज़रूरत से ज़्यादा जटिल हो जाता है
- पुराने Kindle को smart home display की तरह इस्तेमाल करने पर Matt Healy की पोस्ट और पुराने Nook को iCloud photo frame की तरह इस्तेमाल करने पर Ben Borgers की पोस्ट से यह विचार मिला
Kindle पर इमेज दिखाने तक का सफर
- पहला कदम Kindle को jailbreak करके USBNet चालू करना और SSH access सक्षम बनाना था
- इसके बाद cron job के जरिए डिस्प्ले को समय-समय पर refresh किया गया
- mobileread wiki और forum की Kindle hacking सामग्री संदर्भ के रूप में इस्तेमाल हुई
- BART का Real Time Departure पेज चुने गए स्टेशन की अगली ट्रेनें और auto-refresh देता था, इसलिए शुरुआती data source के रूप में उपयुक्त था
- Kindle CLI के
eipsसे PNG दिखाने की कोशिश की गई, लेकिन शुरुआत में इमेज बहुत खिंच जाती थी या कट जाती थी, इसलिए उपयोग योग्य नहीं थी - समस्या यह थी कि Kindle display tool 24-bit RGB PNG को सही तरह से प्रोसेस नहीं करता था और उसे 8-bit इमेज की तरह पढ़ता था
- ImageMagick
convertमें-colorspace gray -depth 8लगाने पर इमेज सही दिखने लगी - Kindle screen size के अनुरूप इमेज और 8-bit color depth मुख्य शर्तें निकलीं
- ImageMagick
पहला implementation: Node.js, Puppeteer, Raspberry Pi
- शुरुआती server में Node.js और Puppeteer से BART वेबपेज के आवश्यक हिस्से का capture लिया जाता था
- capture की गई इमेज को Kindle screen size और 8-bit grayscale के अनुसार बदलकर HTTP endpoint से लौटाया जाता था
- server Raspberry Pi पर चलता था, और Kindle cron के जरिए हर मिनट इमेज लाकर
eipsसे दिखाता था - चूँकि BART पेज खुद auto-refresh सपोर्ट करता था, इसलिए server start पर पेज को एक बार खोलकर रखा जाता था और हर request पर उसे दोबारा load नहीं किया जाता था
- यह संस्करण कुछ समय तक ठीक चला, लेकिन refresh कभी-कभी रुक जाता था या server memory की कमी के कारण restart माँगता था
MUNI विस्तार से सामने आई सीमाएँ
- सैन फ्रांसिस्को बे एरिया में 27 public transit operators हैं, और पहला implementation केवल क्षेत्रीय rapid transit BART को संभालता था
- बाद में MUNI की बस और light rail स्टॉप की arrival times भी साथ में देखना चाहा गया
- MUNI वेबपेज भी स्टॉप-वार arrival times देता था, इसलिए शुरुआत में कई स्टॉप पेजों को Puppeteer से capture करके ImageMagick से जोड़ने की कोशिश की गई
- स्क्रीन के 7 sections में से हर एक, Puppeteer द्वारा बनाए रखे जाने वाले browser tab के बराबर था
- Raspberry Pi में पर्याप्त memory नहीं थी और Chrome हल्का नहीं था
- MUNI पेज पर बार-बार request भेजने पर HTTP 429 मिलता था
- BART और MUNI को साथ दिखाने वाली स्क्रीन, BART-only स्क्रीन की तुलना में कहीं अधिक अस्थिर थी, और एक नज़र में देखने योग्य डिस्प्ले के लिए अधिक विश्वसनीय संरचना चाहिए थी
Rust में दोबारा डिजाइन की गई संरचना
- rewritten version में Puppeteer हटा दिया गया और Rust HTTP server सीधे PNG generate करने लगा
- public interface को Node.js server जैसा ही रखा गया, लेकिन internal implementation पूरी तरह बदल गया
- इसके तीन मुख्य हिस्से थे
- 511.org से अगली arrival times लाना
- arrival times को 8-bit PNG इमेज में render करना
- तैयार PNG को HTTP server से लौटाना
- HTTP server के लिए Axum इस्तेमाल किया गया
- PNG rendering के लिए rust-skia इस्तेमाल हुआ
- Skia, Chrome की आधारभूत graphics library भी है, इसलिए Puppeteer की तुलना में कम overhead के साथ rendering तकनीक का उपयोग संभव हुआ
- अंतिम server code लगभग 1,200 lines का था, और उसमें से केवल महत्वपूर्ण हिस्से संक्षेप में बताए गए
511.org Stop Monitoring API से डेटा लाना
- 511.org का Stop Monitoring API बे एरिया स्टॉप्स के expected arrival और departure times को XML और JSON में देता है
- इस्तेमाल किया गया endpoint इस रूप में था:
http://api.511.org/transit/StopMonitoring/…] agency27 बे एरिया public transit operators में से एक को चुनता है, और SF MUNI का codeSFहै- API एक बार में केवल एक operator का data ला सकता है
- यह किसी एक खास स्टॉप की arrival times या उस operator के सभी स्टॉप्स का data लौटाता है
- कई स्टॉप्स के किसी subset को एक साथ लाने का तरीका नहीं मिला
- 511.org API की limit प्रति घंटे 60 requests है, इसलिए हर स्टॉप के लिए कई requests वाला तरीका real-time display के लिए उपयुक्त नहीं था
- SF MUNI का पूरा data माँगने पर HTTP request में लगभग 5 seconds लगे और लगभग 27 MiB data मिला, जबकि formatted JSON 10 लाख lines से अधिक था
- पूरे response में लगभग 26k
MonitoredStopVisititems थे, लेकिन वास्तविक स्क्रीन के लिए केवल चुने गए स्टॉप और route केLineRef,DirectionRef,DestinationName,StopPointRef,ExpectedArrivalTime,DestinationDisplayकी ज़रूरत थी
Rust data processing में मिली बारीक समस्याएँ
reqwest,serde,serde_json,chrono,tokio,eyreका उपयोग HTTP requests, JSON parsing, time calculation, async execution, और error handling के लिए किया गया- MUNI stop ID, SF MUNI वेबसाइट के stop page पर देखे जा सकते हैं
- उदाहरण के लिए Persia Ave & Paris St के लिए inbound
15918और outbound15919अलग-अलग हैं - MUNI stops दिशा के हिसाब से अलग ID रखते हैं
- उदाहरण के लिए Persia Ave & Paris St के लिए inbound
- 511.org response, JSON के आगे UTF-8 Byte Order Mark जोड़कर लौटाता था, और सीधे
.json()से parse करने परexpected value at line 1 column 1error मिलता था .text()से response को UTF-8 string की तरह पढ़ने पर BOM हट जाता था, और फिर उसेserde_json::from_strसे parse किया जा सकता था- filtering के बाद केवल रुचि वाले स्टॉप्स की लगभग 20 arrival entries बचीं, जो शुरुआती 26k entries की तुलना में कहीं अधिक manageable थीं
LineRefऔरDirectionRefके आधार पर group करके,ExpectedArrivalTimeके अनुसार sort किया गया और UTC timestamps को “कितने मिनट बाद” के रूप में बदला गया
Skia से Kindle के लिए PNG बनाना
- PNG rendering को
skia-safeसे बनाया गया - Kindle screen size 1024x758 के अनुरूप
Gray8bitmap बनाया गया, बैकग्राउंड सफेद भरा गया, फिर text और lines draw की गईं - पहले “Hello World” दिखाने वाला PNG8 इमेज बनाकर rendering pipeline की जाँच की गई
- उसके बाद fake arrival time data से inbound और outbound दो columns वाली table-जैसी इमेज बनाई गई
- जब इसे असली API data से जोड़ा गया, तो test data की तुलना में destination और arrival time अधिक विविध निकले, और fixed layout में text overlap होने लगा
- तेज़ iteration और API limits से बचने के लिए response को
data.jsonमें cache करने वाला अस्थायी code जोड़ा गया
display layout को निखारना
- शुरुआती वास्तविक data स्क्रीन में हर row में अलग मात्रा में information होने के कारण readability कम थी
- किए गए सुधार इस प्रकार थे
- arrival times को right-align किया गया ताकि table के अंत की खाली जगह कम हो
- हर time के बाद का
minहटाकर केवल सूची के अंत मेंminरखा गया - केवल अगली 3 गाड़ियाँ दिखाई गईं
- लंबे route नामों को ध्यान में रखकर destination name को दाईं ओर खिसकाया गया
- इसके बाद route ID को गोल कोनों वाले gray bubble की तरह दिखाने के लिए
measure_strसे text boundary निकाली गई - destination string को route ID bubble के दाएँ किनारे से 15 pixels दूर रखा गया
- ऊपर हल्का gray header जोड़ा गया और
Muni Inbound,Muni Outboundको center-align करके दिखाया गया
Axum से PNG serve करना
- HTTP server
Axum 0.7से बनाया गया और/stops.pngroute पर PNG लौटाया गया - response header को
Content-Type: image/pngपर सेट किया गया - शुरुआत में खाली body के साथ HTTP 200 की पुष्टि की गई, फिर image generation function को जोड़कर PNG bytes को सीधे memory से लौटाया गया
- Kindle पर
wgetसे इमेज लाकरeipsसे दिखाने पर शुरुआत में इमेज rotate होकर कट जाती थी - Kindle डिफ़ॉल्ट रूप से portrait orientation में काम करता है, इसलिए landscape की तरह दिखाने के लिए render करने से पहले rotation matrix लगानी पड़ी
- इमेज size को
(758, 1024)में उलट दिया गया - canvas को
90.0degree घुमाया गया
- इमेज size को
production version में जोड़ी गई अतिरिक्त सुविधाएँ
- वास्तविक उपयोग वाले version में ये सुविधाएँ और जोड़ी गईं
- API data को background में लगातार लाकर HTTP server को तेज़ response देने वाली caching system
- नीचे बाएँ current time और नीचे दाएँ data cache status
- anti-aliasing
- arrival time area से overlap न हो, इसके लिए destination names को truncate करना
- route के अनुसार अलग-अलग gray tones देकर उन्हें जल्दी पहचानने योग्य बनाना
- config file आधारित configuration
- error होने पर error और
.source()chain को PNG पर render करके Kindle पर सीधे दिखाना
- साथ में बनाया गया simplified server code transit-kindle-simplified में उपलब्ध है
- वास्तविक wall-mounted Kindle चलाने वाला server code transit-kindle में है
- BART screenshot-आधारित version लगभग फ़रवरी 2023 में काम कर रहा था, और Rust rewrite के अंतिम UI सुधार जनवरी 2024 में पूरे हुए
1 टिप्पणियां
Hacker News टिप्पणियाँ
मैं OneBusAway के पीछे वाली non-profit संस्था में काम करता/करती हूँ। OneBusAway एक open source प्रोजेक्ट है, जिसे रोज़ लाखों लोग बसों, ट्रेनों, ट्रामों और फनिक्युलर की लोकेशन और arrival times देखने के लिए इस्तेमाल करते हैं
अगर आपके शहर में पहले से OneBusAway server है, तो नए SDK से custom app बना सकते हैं: https://github.com/onebusAway/?q=sdk&type=all&language=&sort...
अगर server नहीं है, तो इस साल बनाए गए Docker image और OpenTofu configuration से GTFS/GTFS-RT feed को REST API में बदला जा सकता है
BART भी GTFS और GTFS-RT उपलब्ध कराता है: https://mobilitydatabase.org/feeds/mdb-53
अमेरिका की दूसरी public transit agencies को भी नए federal rules की वजह से कम-से-कम static timetable data GTFS में सार्वजनिक करना होगा
संबंधित software में योगदान देने वाले developers की हमेशा ज़रूरत है, और खास तौर पर रोज़ 2.5 लाख users के अनुभव को बेहतर बनाने में मदद करने वाले iOS developers की बहुत ज़रूरत है। संपर्क: aaron@onebusaway.org
Docker: https://github.com/OneBusAway/onebusaway-docker / OpenTofu: https://github.com/OneBusAway/onebusaway-deployment
अगर प्रक्रिया सरल हो तो मैं अपना शहर भी जोड़ना चाहूँगा/चाहूँगी। लगता है हाल में libraries और SDK support पर काफी engineering time लगाया गया है, लेकिन नए contributors के लिए समय लगाना आसान हो, इसके लिए developer experience का आसान रास्ता बनाने में निवेश करना बेहतर दिखता है
उदाहरण के लिए यह document काफी जटिल है: https://github.com/OneBusAway/onebusaway/wiki/Multi-Region
“4 मिनट बाद आएगी” दिखता है, लेकिन बस सामने से निकल जाती है; या “5 मिनट late” कहता है, पर असल में 2 मिनट पहले आ जाती है। कभी-कभी बस आती ही नहीं
शायद source data की समस्या हो, लेकिन अंदरूनी तौर पर ऐसा क्या होता है जिससे reliability इतनी कम हो जाती है, यह जानना चाहूँगा/चाहूँगी। संदर्भ के लिए, यह Seattle की बात है
BART और MUNI दोनों General Transit Feed Specification support करते हैं, और यह data पाने का standard तरीका है
https://gtfs.org/documentation/overview/#gtfs-realtime
बहुत पसंद आया। पहले जब मैं Boston में बिना car के रहता/रहती था/थी, तब A से B तक जाने के कई तरीके होते थे और “best” क्या है यह समय या बीच में कहाँ रुकना है, इस पर निर्भर करता था—इसे handle करने के लिए मैंने खुद इससे कहीं ज़्यादा कच्ची चीज़ जोड़-तोड़ कर बनाई थी
उदाहरण के लिए office जाने से पहले बेटे को school छोड़ता/छोड़ती था/थी, और कभी-कभी drop-off के बाद breakfast करता/करती था/थी। अगला departure time देखना अगर होता, तो किसी दिलचस्प जगह पर 4 साल के बच्चे के साथ चलते हुए unpredictable स्थिति में, या ऐसी train पकड़ने के लिए जल्दी करनी चाहिए या नहीं जिसे miss करने पर frequency gap आ जाएगा—ऐसे फैसले कहीं ज़्यादा flexible तरीके से लिए जा सकते थे
हैरानी है कि अभी तक https://oeffi.schildbach.de/index.html का ज़िक्र नहीं हुआ। यह ठीक वही करता है जो यहाँ चाहिए
https://play.google.com/store/apps/details?id=nl.skywave.ovi... / https://apps.apple.com/nl/app/ovinfo/id1144468923
https://drgl.nl/
https://www.rijdendetreinen.nl/
https://spoorkaart.mwnn.nl/
official app से बहुत बेहतर है, और सबसे ज़रूरी जानकारी—नज़दीकी bus stop और अगली bus का real-time time—सीधे दिखा देता है
अगर UK में हैं, तो स्टेशन के departure board की नकल करने वाला departure board device खरीद सकते हैं
https://ukdepartureboards.co.uk/store/product/desktop-depart...
https://tramli.ch/en
मैं भी SF में रहता हूँ, और इसी समस्या को हल करने के लिए A Better Ride नाम का iOS app बनाया है
यह सिर्फ़ आसपास के stops के public transit departure times दिखाता है। लक्ष्य public transit को predictable और explore करने में आसान बनाकर stress कम करना है। यह बिना monetization के खाली समय में बनाया जा रहा personal project है
यह आपको transfers की चिंता किए बिना उन जगहों को explore करने देता है जहाँ आप जा सकते हैं
मैं भी इसी क्षेत्र में hardware बना रहा हूँ और हाल में GTFS के साथ काफ़ी काम किया है, इसलिए तुरंत दिखता है कि transit data parse करने और दिखाने में कितना काम लगा होगा
अगर ठीक हो, तो हर stop से उपलब्ध trips को summarize और group करने से जुड़े UX decisions, और backend के बारे में बात करना चाहूँगा
Kindle को jailbreak करने या images render करने की ज़रूरत नहीं है
अपनी बनाई हुई webpage को Kindle browser में खोलें, और debug command से Kindle के “screensaver” ads या sleep screen को बंद कर दें
browser bar Kindle के एक किनारे पर रह तो जाती है, लेकिन device orientation घुमाकर उसे नीचे या दाईं तरफ़ रखा जा सकता है। इसके बदले weather, transit और news screen को आसान HTML/CSS/JS और अपनी पसंद की backend language से बना सकते हैं, और सस्ते DigitalOcean के 4 डॉलर वाले instance जैसी जगह पर चला सकते हैं
https://blog.notfaqs.com/2018/06/kindle-e-reader-disable-scr...
https://www.mobileread.com/forums/showthread.php?t=198334
document.documentElement.requestFullscreen()से सच में fullscreen में चला गया, status bar भी नहीं दिखता। local public transit API webpage से सीधे accessible थी, इसलिए backend की भी ज़रूरत नहीं पड़ीफिर भी अगर local network के server से communicate करवाएँ, तो ठीक लग रहा है
शायद मैं इस use case को ठीक से समझ नहीं पाया। बात “train लेने” की नहीं है, बल्कि A से B तक जाने की है
अगर train खराब है, strike चल रही है, दोपहर 1–4 बजे line बंद है, या service hours नहीं हैं, तो मुझे alternatives चाहिए। इसलिए मुझे वही चाहिए जो Google Maps देता है, या देने की कोशिश करता है
Ferry Building से Oakland जाना हो और BART खराब हो, तो ferry बेहतर हो सकती है; देर रात हो तो taxi या Uber के अलावा कुछ न हो
Tokyo में Shibuya से Azabujuban जाना हो, तो Ginza Line→Namboku Line, Hanzomon Line→Oedo Line, Yamanote Line→Namboku Line, bus no. 6, taxi जैसे options हैं
हर line के stations और bus stops के बीच भी 3–8 मिनट पैदल चलना पड़ता है, इसलिए अभी आप किसके करीब हैं, यह choice को प्रभावित करता है। आप late हैं या नहीं, luggage बड़ा या भारी है या नहीं, यह भी मायने रखता है
उदाहरण के लिए, किसी जगह जाने के लिए 39 नंबर bus लेकर 15 मिनट पैदल चलना काफी है, लेकिन Google से पूछें तो वह 2 transfers वाला route देता है। किस्मत अच्छी हो तो bus एक बार + walking वाले route जितना ही time लगता है
इसलिए अगर जानना हो कि 39 नंबर bus कब आ रही है, तो मुझे destination जानबूझकर कुछ और बताना पड़ता है
और जहाँ मैं रहता हूँ, वहाँ Google का real-time data संदिग्ध लगता है; operator या कुछ third-party data ज़्यादा भरोसेमंद है
कभी-कभी real time में जबरदस्त planning करनी पड़ती है और ऐसा tool हो तो अच्छा है, लेकिन daily commuting को कहीं ज़्यादा simple tool से भी पर्याप्त रूप से cover किया जा सकता है
आपका Tokyo example भी दिलचस्प है, लेकिन Google के हिसाब से ज़्यादातर public transit options 30–40 मिनट के हैं, इसलिए rough planning में लगभग equivalent हैं। taxi तेज़ है और bicycle भी लगभग वैसी ही है
train खराब हो या strike हो, तो real-time train list में unexpected gap के रूप में दिख जाएगा
शहर में घूमते समय Google Maps खोलकर destination डालता हूँ, लेकिन जिस जगह 500 बार जा चुका हूँ वहाँ घर से निकलते समय उसे खोलने की ज़रूरत नहीं समझता
यह उतना ही meaningful है जितना Disneyland के अंदर के store shopping या dining experience के आधार पर बात करना। Suica tap करना ही दूसरी जगहों से कई गुना तेज़ है, और कई जगह तो support ही नहीं है
इसमें बहुत features हैं, लेकिन Tokyo में कितना support है, पता नहीं। London में अगर home जाने को कहें तो cost, walking distance, speed, transfers, accessibility वगैरह के आधार पर optimized कई तरीके दिखाता है
रीसायकल किए गए Lenovo ThinkSmart View टैबलेट/मीटिंग रूम डिवाइस पर Home Assistant और स्थानीय ट्रांसपोर्ट एजेंसी का इंटीग्रेशन जोड़कर कुछ ऐसा ही इस्तेमाल कर रहा हूँ
फायदा यह है कि यह कहीं ज़्यादा तुरंत इस्तेमाल करने लायक है। बेशक Lenovo पर custom Android ROM डाला है, लेकिन जो चाहें वह दिखा सकते हैं और लाइटिंग या robot vacuum जैसी चीज़ें भी कंट्रोल कर सकते हैं
BART मैप को फिजिकल LED साइनबोर्ड के रूप में बनाया गया एक खूबसूरत प्रोडक्ट भी है: https://www.designrules.co/