Aplicații conștiente de context și arhitectură complexă de procesare a evenimentelor
Publicat: 2022-03-11Utilizarea telefoanelor mobile pe tot globul este în continuă creștere. Începând cu 2013, aproximativ 73% dintre utilizatorii de Internet au consumat conținut prin intermediul unui dispozitiv mobil și este de așteptat ca acest procent să ajungă la aproape 90% până în 2017.
Există, desigur, multe motive pentru revoluția mobilă. Dar una dintre cele mai semnificative este că aplicațiile mobile au, în general, acces la un context mai bogat, deoarece aproape toate smartphone-urile de astăzi sunt echipate cu senzori de locație, senzori de mișcare, bluetooth și wifi. Folosind datele lor, aplicațiile pot obține „conștientizarea contextului” care le poate crește dramatic capacitățile și valoarea și le poate face cu adevărat să iasă în evidență în magazinele de aplicații.
În acest tutorial, vom explora crearea de aplicații conștiente de context printr-un exemplu de procesare a evenimentelor complexe. Vom folosi un exemplu destul de simplu: o aplicație pentru prețul combustibilului care găsește cele mai bune prețuri la combustibil din zona dvs.
Aplicații care țin cont de context
În Designing Calm Technology, Mark Weiser și John Seely Brown descriu tehnologia calmă drept „aceasta care informează, dar nu ne solicită concentrarea sau atenția”.
Aplicațiile mobile conștiente de context sunt foarte în concordanță cu această noțiune și reprezintă un pas important și valoros pe această cale. Ei folosesc informații contextuale obținute de la senzorii lor pentru a oferi utilizatorului în mod proactiv informații valoroase și fac acest lucru cu un efort minim din partea utilizatorului. Mark Weiser și John Seely Brown ar aplauda fără îndoială acest progres tehnologic.
Conștientizarea contextului este ideea că o aplicație poate simți și reacționa pe baza datelor contextuale la care are acces. O astfel de aplicație utilizează date bogate ale senzorilor care sunt disponibile pe un dispozitiv mobil pentru a oferi utilizatorului informații exacte și relevante în contextul adecvat. Prin tendințele pe care le observă pe parcursul utilizării dispozitivului și/sau prin feedback-ul oferit de utilizator, o astfel de aplicație poate de fapt „învăța” în timp, devenind astfel „mai inteligentă” și mai utilă.
Procesare complexă a evenimentelor
Procesarea complexă a evenimentelor (CEP) este o formă de procesare a evenimentelor care utilizează analize mai sofisticate ale mai multor evenimente (adică, de-a lungul timpului, din surse diferite și așa mai departe), integrând și analizând conținutul acestora pentru a deduce informații și modele mai semnificative.
Într-o aplicație mobilă, CEP poate fi aplicat evenimentelor generate de senzorii dispozitivului mobil, precum și surselor de date externe la care are acces aplicația.
Caracteristicile cheie ale aplicației noastre pentru prețul combustibilului
În scopul tutorialului nostru complex de procesare a evenimentelor, să presupunem că funcțiile aplicației noastre pentru prețul combustibilului sunt limitate la următoarele:
- detectarea automată a locațiilor care sunt relevante din punct de vedere geografic pentru utilizator (de exemplu, locația de acasă a utilizatorului și locul de muncă al utilizatorului)
- identificarea automată a stațiilor de alimentare la o distanță rezonabilă de domiciliul și locul de muncă al utilizatorului
- notificând automat utilizatorul cu privire la cele mai bune prețuri la combustibil în apropierea casei și a serviciului
OK, să începem.
Detectarea locațiilor de acasă și de la locul de muncă ale utilizatorului
Să începem cu logica pentru detectarea automată a locațiilor de acasă și de la locul de muncă ale utilizatorului. Pentru a menține lucrurile simple pentru exemplul nostru complex de procesare a evenimentelor, vom presupune că utilizatorul are un program de lucru destul de normal. Prin urmare, putem presupune că utilizatorul va fi de obicei acasă între 2 și 3 dimineața și va fi de obicei la birou între 2 și 3 PM.
Pe baza acestor ipoteze, definim două reguli CEP și colectăm date despre locație și oră de pe smartphone-ul utilizatorului:
- Regula locației acasă
- adunați date despre locație între 2 și 3 dimineața timp de o săptămână
- grupați datele de locație pentru a obține adresa aproximativă de domiciliu
- Regula locației de lucru
- adunați date despre locație între 14 și 15 pentru zilele lucrătoare
- clusterizați datele despre locație pentru a obține locația aproximativă de lucru
Algoritmul de nivel înalt pentru detectarea locațiilor este descris mai jos.
Să presupunem următoarea structură simplă de date JSON pentru datele de locație:
{ "uid": "some unique identifier for device/user", "location": [longitude, latitude] "time": "time in user's timezone" }
Notă: Este întotdeauna o practică bună să faceți imuabile datele senzorului (sau tipul valorii), astfel încât să poată fi utilizate în siguranță de diferite module din fluxul de lucru CEP.
Implementarea
Vom implementa algoritmul nostru folosind un model de modul composabil , în care fiecare modul îndeplinește o singură sarcină și apelează apoi când sarcina este finalizată. Aceasta este conformă cu filosofia Unix Rule of Modularity.
Mai exact, fiecare modul este o funcție care acceptă un obiect de config
și o funcție next
care este apelată pentru a transmite datele către modulul următor. În consecință, fiecare modul returnează o funcție care poate accepta datele senzorului. Iată semnătura de bază a unui modul:
// nominal structure of each composable module function someModule(config, next) { // do initialization if required return function(data) { // do runtime processing, handle errors, etc. var nextData = SomeFunction(data); // optionally call next with nextData next(nextData); } }
Pentru a implementa algoritmul nostru de deducere a locațiilor de acasă și de la locul de muncă ale utilizatorului, vom avea nevoie de următoarele module:
- Modul de filtru de timp
- Modul acumulator
- Modul de clusterizare
Fiecare dintre aceste module este descris mai detaliat în subsecțiunile care urmează.
Modul de filtru de timp
Filtrul nostru de timp este o funcție simplă care ia ca intrare evenimentele de date despre locație și transmite date către modulul next
numai dacă evenimentul a avut loc în intervalul de timp de interes. Datele de config
pentru acest modul constau, prin urmare, din orele de început și de sfârșit ale intervalului de timp de interes. (O versiune mai sofisticată a modulului ar putea filtra pe baza mai multor intervale de timp.)
Iată o implementare pseudocod a modulului de filtru de timp:
function timeFilter(config, next) { function isWithin(timeval) { // implementation to compare config.start <= timeval <= config.end // return true if within time slice, false otherwise } return function (data) { if(isWithin(data.time)) { next(data); } }; }
Modul acumulator
Responsabilitatea acumulatorului este pur și simplu să adune date de locație pentru a fi apoi transmise la modulul next
. Această funcție menține o găleată internă de dimensiuni fixe pentru stocarea datelor. Fiecare locație nouă întâlnită este adăugată în găleată până când găleata este plină. Datele de locație acumulate în găleată sunt apoi trimise la modulul următor ca o matrice.
Sunt acceptate două tipuri de găleți pentru acumulatori. Tipul compartimentului afectează ceea ce se face cu conținutul compartimentului după ce datele sunt transmise la următoarea fază, după cum urmează:
Tumbling window bucket (
type = 'tumbling'
): după redirecționarea datelor, golește întreaga găleată și începe proaspăt (dimensiunea găleții redusă înapoi la 0)Tipul ferestrei de rulare (
type = 'running'
): după redirecționarea datelor, elimină doar cel mai vechi element de date din compartiment (reduce dimensiunea compartimentului cu 1)
Iată o implementare de bază a modulului acumulator:
function accumulate(config, next) { var bucket = []; return function (data) { bucket.unshift(data); if(bucket.length >= config.size) { var newSize = (config.type === 'tumbling' ? 0 : bucket.length - 1); next(bucket.slice(0)); bucket.length = newSize; } }; }
Modul de clusterizare
Există, desigur, multe tehnici sofisticate în geometria coordonatelor pentru a clusteriza datele 2D. Iată o modalitate simplă de a grupa datele despre locație:
- găsiți vecini pentru fiecare locație într-un set de locații
- dacă unii dintre vecini aparțin unui cluster existent, atunci extindeți vecinii cu cluster
- dacă locațiile din setul de vecini sunt mai mari decât pragul, adăugați vecini ca un cluster nou
Iată o implementare a acestui algoritm de grupare (folosind Lo-Dash
):
var _ = require('lodash'); function createClusters(location_data, radius) { var clusters = []; var min_points = 5; // Minimum cluster size function neighborOf(this_location, all_locations) { return _.filter(all_locations, function(neighbor) { var distance = distance(this_point.location, neighbor.location); // maximum allowed distance between neighbors is 500 meters. return distance && (500 > distance); } } _.each(location_data, function (loc_point) { // Find neighbors of loc_point var neighbors = neighborOf(loc_point, location_data, radius); _.each(clusters, function (cluster, index) { // Check whether some of the neighbors belong to cluster. if(_.intersection(cluster, neighbors).length){ // Expand neighbors neighbors = _.union(cluster, neighbors); // Remove existing cluster. We will add updated cluster later. clusters[index] = void 0; } }); if(neighbors.length >= min_points){ // Add new cluster. clusters.unshift(neighbors); } }); return _.filter(clusters, function(cluster){ return cluster !== void 0; }); }
Codul de mai sus presupune existența unei funcții distance()
care calculează distanța (în metri) dintre două locații geografice. Acceptă două puncte de locație sub formă de [longitude, latitude]
și returnează distanța dintre ele. Iată un exemplu de implementare a unei astfel de funcții:
function distance(point1, point2) { var EARTH_RADIUS = 6371000; var lng1 = point1[0] * Math.PI / 180; var lat1 = point1[1] * Math.PI / 180; var lng2 = point2[0] * Math.PI / 180; var lat2 = point2[1] * Math.PI / 180; var dLat = lat2 - lat1; var dLon = lng2 - lng1; var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); var arc = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var distance = EARTH_RADIUS * arc; return distance; }
Cu algoritmul nostru de clustering definit și implementat (în funcția noastră createClusters()
prezentată mai devreme), îl putem folosi ca bază pentru modulul nostru de clustering:
function clusterize(config, next) { return function(data) { var clusters = createClusters(data, config.radius); next(clusters); }; }
Tragând totul împreună
Toate funcțiile componente necesare sunt acum definite, așa că suntem gata să codificăm regulile noastre de locație acasă/serviciu.
Iată, de exemplu, o posibilă implementare a regulii locației acasă:
var CLUSTER_RADIUS = 150; // use cluster radius of 150 meters var BUCKET_SIZE = 500; // collect 500 location points var BUCKET_TYPE = 'tumbling'; // use a tumbling bucket in our accumulator var home_cluster = clusterize({radius: CLUSTER_RADIUS}, function(clusters) { // Save clusters in db }); var home_accumulator = accumulate({size: BUCKET_SIZE, type: BUCKET_TYPE}, home_cluster); var home_rule = timeFilter({start: "2AM", end: "3AM"}, home_accumulator);
Acum, ori de câte ori sunt primite date de locație de la telefonul inteligent (prin websocket, TCP, HTTP), transmitem aceste date către funcția home_rule
care, la rândul său, detectează clustere pentru casa utilizatorului.
„Locația de acasă” a utilizatorului se presupune că este centrul clusterului de locații de acasă.
Notă: Deși acest lucru poate să nu fie complet precis, este adecvat pentru exemplul nostru simplu, mai ales că scopul acestei aplicații este, în orice caz, pur și simplu să cunoască zona din jurul casei utilizatorului, mai degrabă decât să cunoască locația exactă a domiciliului utilizatorului.
Iată un exemplu de funcție simplă care calculează „centrul” unui set de puncte dintr-un cluster în medie la latitudini și longitudini ale tuturor punctelor din setul de cluster:
function getCentre(cluster_data) { var len = cluster_data.length; var sum = _.reduce(cluster_data, function(memo, cluster_point){ memo[0] += cluster_point[0]; memo[1] += cluster_point[1]; return memo; }, [0, 0]); return [sum[0] / len, sum[1] / len]; }
O abordare similară ar putea fi folosită pentru deducerea locului de lucru, singura diferență fiind că ar folosi un filtru de timp între 2 și 3 PM (spre deosebire de 2 și 3 AM).
Aplicația noastră de combustibil este astfel capabilă să detecteze automat locurile de muncă și de acasă ale utilizatorului, fără a necesita nicio intervenție a utilizatorului. Acesta este un calcul conștient de context în cea mai bună calitate!
Găsirea benzinării din apropiere
Munca grea pentru a stabili gradul de conștientizare a contextului a fost acum făcută, dar avem încă nevoie de o regulă pentru a identifica prețurile stațiilor de alimentare pe care să le monitorizăm (adică, care stații de alimentare sunt suficient de aproape de casa sau locul de muncă al utilizatorului pentru a fi relevante). Această regulă necesită acces la toate locațiile stațiilor de alimentare pentru toate regiunile acceptate de aplicația de combustibil. Regula este următoarea:
- Regula benzinăriei
- găsiți cele mai apropiate stații de alimentare pentru fiecare casă și loc de muncă
Acest lucru poate fi implementat cu ușurință utilizând funcția de distanță prezentată mai devreme ca filtru de locație pentru a se aplica tuturor stațiilor de benzină cunoscute de aplicație.
Monitorizarea prețurilor la combustibil
Odată ce aplicația de combustibil obține lista de stații de combustibil preferate (adică, din apropiere) pentru utilizator, poate urmări cu ușurință cele mai bune prețuri la combustibil la aceste stații. De asemenea, poate anunța utilizatorul când una dintre aceste benzinării are prețuri sau oferte speciale, mai ales când utilizatorul este detectat că se află în apropierea acestor benzinării.
Concluzie
În acest tutorial complex de procesare a evenimentelor, abia am zgâriat suprafața calculului conștient de context.
În exemplul nostru simplu, am adăugat contextul locației unei aplicații de raportare a prețului combustibilului, altfel simplă, și am făcut-o mai inteligentă. Aplicația se comportă acum diferit pe fiecare dispozitiv și în timp detectează tiparele de locație pentru a îmbunătăți automat valoarea informațiilor pe care le oferă utilizatorilor săi.
Cu siguranță, pot fi adăugate mult mai multe date de logică și senzori pentru a crește acuratețea și utilitatea aplicației noastre care ține cont de context. Un dezvoltator de telefonie mobilă inteligent ar putea, de exemplu, să folosească datele rețelelor sociale, datele meteorologice, datele privind tranzacțiile terminalului POS și așa mai departe pentru a adăuga și mai multă conștientizare a contextului aplicației noastre și a o face mai viabilă și mai comercializabilă.
Cu calculul conștient de context, posibilitățile sunt nesfârșite. Tot mai multe aplicații inteligente vor continua să apară în magazinele de aplicații care folosesc această tehnologie puternică pentru a ne simplifica viața.