Aplicativos sensíveis ao contexto e arquitetura de processamento de eventos complexos

Publicados: 2022-03-11

O uso de telefones celulares em todo o mundo está aumentando constantemente. A partir de 2013, cerca de 73% dos usuários de Internet consumiam conteúdo por meio de um dispositivo móvel e esse percentual deve chegar perto de 90% até 2017.

Há, é claro, muitas razões para a revolução móvel. Mas um dos mais significativos é que os aplicativos móveis geralmente têm acesso a um contexto mais rico, já que quase todos os smartphones hoje estão equipados com sensores de localização, sensores de movimento, bluetooth e wifi. Ao fazer uso de seus dados, os aplicativos podem obter “consciência de contexto” que pode aumentar drasticamente seus recursos e valor, e pode realmente fazer com que eles se destaquem nas lojas de aplicativos.

Neste tutorial, exploraremos a criação de aplicativos sensíveis ao contexto por meio de um exemplo complexo de processamento de eventos. Usaremos um exemplo bastante simples: um aplicativo de preço de combustível que encontra os melhores preços de combustível em sua área.

O reconhecimento de contexto em aplicativos pode ser criado por meio de um tutorial de processamento de eventos complexo como este.

Aplicativos sensíveis ao contexto

Em Designing Calm Technology, Mark Weiser e John Seely Brown descrevem a tecnologia calma como “aquela que informa, mas não exige nosso foco ou atenção”.

Os aplicativos móveis sensíveis ao contexto são altamente consistentes com essa noção e são um passo importante e valioso nesse caminho. Eles empregam informações contextuais coletadas de seus sensores para fornecer proativamente ao usuário informações valiosas e o fazem com o mínimo de esforço por parte do usuário. Mark Weiser e John Seely Brown, sem dúvida, aplaudiriam esse avanço tecnológico.

Consciência de contexto é a ideia de que um aplicativo pode detectar e reagir com base nos dados contextuais aos quais tem acesso. Esse aplicativo faz uso de dados de sensores avançados que estão disponíveis em um dispositivo móvel para fornecer informações precisas e relevantes ao usuário no contexto apropriado. Por meio de tendências que observa ao longo do uso do dispositivo e/ou por meio de feedback fornecido pelo usuário, tal aplicativo pode realmente “aprender” com o tempo, tornando-se assim “mais inteligente” e mais útil.

Processamento de eventos complexos

O processamento de eventos complexos (CEP) é uma forma de processamento de eventos que emprega análises mais sofisticadas de vários eventos (ou seja, ao longo do tempo, de diferentes fontes e assim por diante), integrando e analisando seu conteúdo para deduzir informações e padrões mais significativos.

Em um aplicativo móvel, o CEP pode ser aplicado a eventos gerados a partir dos sensores do dispositivo móvel, bem como a fontes de dados externas às quais o aplicativo tem acesso.

Principais recursos do nosso aplicativo de preços de combustível

Para fins de nosso tutorial complexo de processamento de eventos, vamos supor que os recursos do nosso aplicativo de preço de combustível sejam limitados ao seguinte:

  • detectar automaticamente locais geograficamente relevantes para o usuário (por exemplo, local de residência do usuário e local de trabalho do usuário)
  • identificando automaticamente os postos de combustível a uma distância razoável da casa e dos locais de trabalho do usuário
  • notificando automaticamente o usuário dos melhores preços de combustível perto de casa e do trabalho

Certo, vamos começar.

Detectando os locais de casa e trabalho do usuário

Vamos começar com a lógica para detectar automaticamente os locais de residência e trabalho do usuário. Para manter as coisas simples para nosso exemplo de processamento de eventos complexo, vamos supor que o usuário tenha um cronograma de trabalho bastante normal. Portanto, podemos supor que o usuário normalmente estará em casa entre 2 e 3 da manhã e normalmente estará em seu escritório entre 14 e 15h.

Com base nessas premissas, definimos duas regras de CEP e coletamos dados de localização e horário do smartphone do usuário:

  • Regra de localização inicial
    • coletar dados de localização entre 2 e 3 da manhã por uma semana
    • agrupar dados de localização para obter o endereço residencial aproximado

  • Regra do local de trabalho
    • coletar dados de localização entre 14h e 15h durante a semana
    • clusterize os dados de localização para obter a localização aproximada do trabalho

O algoritmo de alto nível para detectar locais é descrito abaixo.

Este diagrama demonstra como o aplicativo com reconhecimento de contexto neste tutorial funcionará.

Vamos supor a seguinte estrutura de dados JSON simples para dados de localização:

 { "uid": "some unique identifier for device/user", "location": [longitude, latitude] "time": "time in user's timezone" }

Observação: é sempre uma boa prática tornar os dados do sensor imutáveis ​​(ou tipo de valor), para que possam ser usados ​​com segurança por diferentes módulos no fluxo de trabalho do CEP.

Implementação

Implementaremos nosso algoritmo usando um padrão de módulo composto , em que cada módulo executa apenas uma tarefa e chama next quando a tarefa é concluída. Isso está de acordo com a filosofia Unix Rule of Modularity.

Especificamente, cada módulo é uma função que aceita um objeto de config e uma função next que é chamada para passar os dados para o próximo módulo. Assim, cada módulo retorna uma função que pode aceitar dados do sensor. Aqui está a assinatura básica de um módulo:

 // 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); } }

Para implementar nosso algoritmo para deduzir os locais de residência e trabalho do usuário, precisaremos dos seguintes módulos:

  • Módulo de filtro de tempo
  • Módulo acumulador
  • Módulo de cluster

Cada um desses módulos é descrito com mais detalhes nas subseções a seguir.

Módulo de filtro de tempo

Nosso filtro de tempo é uma função simples que recebe eventos de dados de localização como entrada e só passa os dados para o next módulo se o evento ocorreu dentro do intervalo de tempo de interesse. Os dados de config para este módulo, portanto, consistem nos horários de início e término da fatia de tempo de interesse. (Uma versão mais sofisticada do módulo pode filtrar com base em várias fatias de tempo.)

Aqui está uma implementação de pseudocódigo do módulo de filtro de tempo:

 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); } }; }

Módulo acumulador

A responsabilidade do acumulador é simplesmente coletar dados de localização para então serem passados ​​para o next módulo. Essa função mantém um bucket interno de tamanho fixo para armazenar dados. Cada novo local encontrado é adicionado ao bucket até que o bucket esteja cheio. Os dados de localização acumulados no bucket são então enviados para o próximo módulo como um array.

Há suporte para dois tipos de buckets de acumulador. O tipo de bucket afeta o que é feito com o conteúdo do bucket depois que os dados são encaminhados para a próxima fase, da seguinte forma:

  • Caçamba da janela em cascata ( type = 'tumbling' ): após o encaminhamento de dados, esvazia todo o bucket e começa de novo (tamanho do bucket reduzido de volta a 0)

  • Tipo de janela em execução ( type = 'running' ): após o encaminhamento de dados, apenas descarta o elemento de dados mais antigo no bucket (reduz o tamanho do bucket em 1)

Aqui está uma implementação básica do módulo acumulador:

 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; } }; }

Módulo de cluster

É claro que existem muitas técnicas sofisticadas em geometria de coordenadas para agrupar dados 2D. Aqui está uma maneira simples de agrupar dados de localização:

  • encontrar vizinhos para cada local em um conjunto de locais
  • se alguns dos vizinhos pertencem a um cluster existente, expanda os vizinhos com cluster
  • se os locais no conjunto de vizinhos forem maiores que o limite, adicione vizinhos como um novo cluster

Aqui está uma implementação deste algoritmo de clustering (usando 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; }); }

O código acima pressupõe a existência de uma função distance() que calcula a distância (em metros) entre duas localizações geográficas. Ele aceita dois pontos de localização na forma de [longitude, latitude] e retorna a distância entre eles. Aqui está um exemplo de implementação de tal função:

 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; }

Com nosso algoritmo de clustering definido e implementado (em nossa função createClusters() mostrada anteriormente), podemos usá-lo como base para nosso módulo de clustering:

 function clusterize(config, next) { return function(data) { var clusters = createClusters(data, config.radius); next(clusters); }; }

Puxando tudo junto

Todas as funções de componentes necessárias agora estão definidas, então estamos prontos para codificar nossas regras de localização de casa/trabalho.

Aqui, por exemplo, está uma possível implementação da regra de localização inicial:

 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);

Agora, sempre que os dados de localização são recebidos do smartphone (via websocket, TCP, HTTP), encaminhamos esses dados para a função home_rule que, por sua vez, detecta clusters para a casa do usuário.

A “localização inicial” do usuário é então considerada o centro do cluster de localização inicial.

Nota: Embora isso possa não ser totalmente preciso, é adequado para nosso exemplo simples, especialmente porque o objetivo deste aplicativo em qualquer caso é simplesmente conhecer a área ao redor da casa do usuário, em vez de saber a localização exata da casa do usuário.

Aqui está uma função de exemplo simples que calcula o “centro” de um conjunto de pontos em um cluster pela média das latitudes e longitudes de todos os pontos no conjunto de clusters:

 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]; }

Uma abordagem semelhante poderia ser empregada para deduzir o local de trabalho, com a única diferença de que usaria um filtro de tempo entre 14h e 15h (em oposição a 2h e 3h).

Nosso aplicativo de combustível é capaz de detectar automaticamente os locais de trabalho e residência do usuário sem exigir qualquer intervenção do usuário. Esta é a computação sensível ao contexto no que há de melhor!

Encontrar postos de combustível próximos

O trabalho árduo para estabelecer a conscientização do contexto já foi feito, mas ainda precisamos de mais uma regra para identificar quais preços de postos de combustível monitorar (ou seja, quais postos de combustível estão próximos o suficiente da casa ou local de trabalho do usuário para serem relevantes). Esta regra precisa de acesso a todas as localizações de postos de combustível para todas as regiões suportadas pelo aplicativo de combustível. A regra é a seguinte:

  • Regra do posto de combustível
    • encontre os postos de combustível mais próximos para cada local de residência e trabalho

Isso pode ser facilmente implementado usando a função de distância mostrada anteriormente como um filtro de localização para aplicar a todos os postos de combustível conhecidos pelo aplicativo.

Os aplicativos sensíveis ao contexto ficam mais inteligentes com o tempo, como este aplicativo tutorial.

Monitoramento de preços de combustível

Uma vez que o aplicativo de combustível obtém a lista de postos de combustível preferidos (ou seja, próximos) para o usuário, ele pode facilmente observar os melhores preços de combustível nesses postos. Ele também pode notificar o usuário quando um desses postos de combustível tiver preços ou ofertas especiais, especialmente quando o usuário for detectado próximo a esses postos de combustível.

Este tutorial de processamento de eventos complexo demonstra como o reconhecimento de contexto pode ser criado em um aplicativo.

Conclusão

Neste tutorial complexo de processamento de eventos, mal arranhamos a superfície da computação sensível ao contexto.

Em nosso exemplo simples, adicionamos contexto de localização a um aplicativo de relatório de preços de combustível simples e o tornamos mais inteligente. O aplicativo agora se comporta de maneira diferente em cada dispositivo e, com o tempo, detecta os padrões de localização para melhorar automaticamente o valor das informações que fornece aos usuários.

Certamente muito mais dados lógicos e de sensores podem ser adicionados para aumentar a precisão e a utilidade do nosso aplicativo sensível ao contexto. Um desenvolvedor móvel inteligente poderia, por exemplo, fazer uso de dados de redes sociais, dados meteorológicos, dados de transações de terminais POS e assim por diante para adicionar ainda mais reconhecimento de contexto ao nosso aplicativo e torná-lo mais viável e comercializável.

Com a computação sensível ao contexto, as possibilidades são infinitas. Cada vez mais aplicativos inteligentes continuarão a aparecer nas lojas de aplicativos que empregam essa poderosa tecnologia para tornar nossas vidas mais simples.