Wednesday, December 30, 2015

Alterando uma extensão do ThingWorx - Faltou falar de autenticação!

Autenticando!

Pessoal, anteriormente (leiam os posts Como Escolher, Hello World, IDE e Runtime) falei sobre o uso da API HeatMaps do Google mas acabei me esquecendo de um ponto importante: a autenticação!

Como mencionei no primeiro post, o uso da API Heatmaps tem uma cota diária e para tal é necessário fazer a autenticação utilizando-se uma Chave de Autenticação criada para liberar a API e controlar o uso.

Para isso é necessário entrar no Console de Desenvolvedores do Google e criar uma credencial que será associada à API e depois utilizada na aplicação. No help do console há algumas dicas de como fazê-lo.

No meu caso eu criei uma Browser Key (ainda não entendi bem a diferença entre Browser/Server keys),  e como o ThingWorx usa widgets, há um arquivo XML chamado metadata que possui algumas configurações, e uma delas é a chamada da API. Tudo o que tive que fazer foi adicionar a autenticação:

<Widget name="googlemap">
<UIResources>
<!-- Studio ONLY -->
<FileResource type="CSS" file="googlemap.ide.css" description="" isDevelopment="true" isRuntime="false" />
<FileResource type="JS" file="googlemap.ide.js" description="" isDevelopment="true" isRuntime="false" />
<!-- Runtime/Squeal ONLY -->
<FileResource type="CSS" file="googlemap.runtime.css" description="" isDevelopment="false" isRuntime="true" />
<FileResource type="JS" file="googlemap.runtime.js" description="" isDevelopment="false" isRuntime="true" />
<!--FileResource type="JS" url="https://maps.google.com/maps/api/js?sensor=false" description="" isDevelopment="false" isRuntime="true" /-->
<FileResource type="JS" url="https://maps.googleapis.com/maps/api/js?key=SUA_CHAVE_DE_AUTENTICAÇÃO_AQUI&amp;libraries=visualization" description="" isDevelopment="false" isRuntime="true"  />
</UIResources>
</Widget>

Só consegui fazer funcionar para uma instância na nuvem, pois coloco a URL como autorizada. Não consegui autorizar o meu localhost.

No meu próximo post eu falo sobre a criação da aplicação, a configuração do Widget e como fica tudo isso!

PS: Pensando sobre esse approach acabei descobrindo uma falha de segurança: como coloco a chave de autenticação aí, no momento de runtime dá pra capturar essa chave usando o console do browser e usar para outra coisa. Na verdade, na configuração da chave eu digo quais são as URLs autorizadas, reduzindo o risco de uso indevido, mas me incomoda o fato de a chave estar exposta publicamente:





Cheers
Ewerton

Alterando uma extensão do ThingWorx - Criando funções em Runtime e fazendo as chamadas da API HeatmapLayer

Criando a função para a execução (Runtime)

Fala galera, em meu post anterior eu descrevi como criei as propriedades que serão consumidas nas chamadas da API em Runtime. Agora vou descrever como ficou a função que chama a API e alguns detalhes e dificuldades que tive. Fiquei surpreso em ver o tamanho do código depois que fiz uma limpeza: sofri tanto para deixar funcionando que nem parece que foi só isso que fiz. :)

Antes...

Também decidi deixar o código mais flexível e para isso criei mais duas propriedades: CustomLayerDataField, CustomLayerLocationField. Isso permitirá escolher qual o campo irá ser usado para os dados e para a localização, sem depender de um Datashape específico. Também criei outras propriedades para a chamada da API, mas isso é mais para configurações de raio, opacidade, etc.

Aqui como ficaram as propriedades:

'CustomLayerDataField': {
'description' : 'Field which will provide data for Custom Layer',
'isBindingTarget': true,
'isVisible': true,
'isEditable': true,
'defaultValue': 'Reading',
'sourcePropertyName': 'CustomLayerData',
'baseTypeRestriction': 'NUMBER',
'baseType': 'FIELDNAME'
},
'CustomLayerLocationField': {
'description' : 'Field which will provide data for Custom Layer',
'isBindingTarget': true,
'isVisible': true,
'isEditable': true,
'defaultValue': 'location',
'sourcePropertyName': 'CustomLayerData',
'baseTypeRestriction': 'LOCATION',
'baseType': 'FIELDNAME'
},

Vejam que há outras características importantes:
sourcePropertyName - relaciona a outra propriedade e define a lista de propriedades que serão exibidas;
baseTypeRestriction - restringe o tipo que será mostrado na lista.

Em outras palavras, ao relacionar-se os dados ao campo CustomLayerData, as propriedades desse campo estão disponíveis para seleção, mas apenas as propriedades do tipo selecionado.






Agora sim, a função para Runtime 

Ficou assim:

if (updatePropertyInfo.TargetProperty === 'CustomLayerData') {
var customLayerDataRows = updatePropertyInfo.ActualDataRows;
var nRows = customLayerDataRows.length;
var heatmapData=[];
for (var rowNumber = 0; rowNumber < nRows; rowNumber++) {
var row = customLayerDataRows[rowNumber];
heatmapData[rowNumber] = 
{location: new google.maps.LatLng(parseFloat(row[this.getProperty('CustomLayerLocationField')].latitude) , parseFloat(row[this.getProperty('CustomLayerLocationField')].longitude)), weight: parseFloat(row[this.getProperty('CustomLayerDataField')])};
}  
var heatMapDissipating = this.getProperty('heatMapDissipating');
var heatMapGradient = this.getProperty('heatMapGradient');
var heatMapOpacity= this.getProperty('heatMapOpacity')
var heatMapRadius=this.getProperty('heatMapRadius')
var heatmap = new google.maps.visualization.HeatmapLayer({
data: heatmapData,
dissipating: heatMapDissipating,
opacity: heatMapOpacity,
radius: heatMapRadius
});
heatmap.setMap(this.map); 
return;
}

Algumas considerações:

  • Para usar o valor de uma propriedade, usei o método getProperty, como em: this.getProperty('CustomLayerLocationField')
    • Nesse caso específico irei pegar o nome da coluna definida nesse campo para que seja usada como location no mapa.
  • No caso acima, o Google Maps necessita dos valores de lat e long separados, por isso ao consumir esses dados foi usado: this.getProperty('CustomLayerLocationField')].latitude
  • O bloco for está iterando todos os dados associados ao Widget e criando um vetor (heatmapData) para que esse seja passado na chamada da API. Esse vetor possui um Object LatLong do GoogleMaps
    • Na tag location, estão sendo passados os valores de lat e long, como mencionei antes.
    • Na tag weight está sendo passado o valor dos pontos que serão plotados, com seu devido peso. Quanto maior, mais "vermelho" é o mapa.
O resultado no Thingworx ficou assim:

Tive várias dificuldades, muitas delas em como acessar os dados de uma matriz (pegar a lat e long do campo location, por exemplo) e outras com sintaxe de chamadas.

Vou continuar trabalhando nessa alteração, mas acho que isso já dá pra começar! Espero que ajude!

Cheers!
Ewerton

PS.. Faltou falar de Autenticação, que cobri no meu próximo post



Tuesday, December 29, 2015

Alterando uma extensão do ThingWorx - Criando propriedades para o Widget

Criando  propriedades para o Widget do Google Maps

Como mencionei em meu post anterior, o ThingWorx possibilita o uso de elementos de interface pré criados, chamados Widgets, na criação das telas de interface, chamadas de Mashups.

Esses Widgets possuem propriedades e eventos que podem ser utilizados na criação da aplicação utilizando-se drag n' drop no momento da criação do Mashup.

No caso da extensão do Google Maps, o widget foi criado em Javascript e possui 2 arquivos principais. Iremos falar de um deles agora, o googlemap.ide e como alterar para adicionar os parâmetros necessários para a funcionalidade de Heatmaps.

Na sessão de propriedades do Widget, irei criar algumas das propriedades que penso em usar:

  1. Liga/Desliga o Layer de Heatmap - propriedade Boolean apenas para acionar a funcionalidade.
  2. Tipo de Layer: penso em fazer com que a alteração seja flexível, então irei adicionar opções para se encolher entre criar-se um layer KML, um layer usando a API HeatMap ou uma FusionTable
  3. Dados de Entrada: é uma matriz com os dados que irão popular o mapa caso utilize-se a API
Vou focar nessas 3 propriedades pois meu desenvolvimento agora será utilizando a API. Quando for desenvolver para Layer KML ou FusionTable precisarei de propriedades para passar esse tipo de dado.

Código para adicionar-se as propriedades:

this.widgetProperties = function () {
return {
'ShowCustomLayer': {
'description': 'Select if custom layer is used',
  'isBindingTarget': true,
'defaultValue': false,
'baseType': 'BOOLEAN'
},
'CustomLayerType': {
  'isBindingTarget': true,
  'description' : 'Select type of custom layer: Google HeatMap or Fusion Tables',
  'baseType': 'STRING',
  'defaultValue': 'HeatMap',
  'selectOptions': [
    { value: 'HeatMap', text: 'HeatMap' },
    { value: 'FusionTables', text: 'Fusion Tables' }
    ]
  },
'CustomLayerData': {
  'description' : 'Data to populate Custom Layer',
  'isBindingTarget': true,
  'isVisible': true,
  'baseType': 'INFOTABLE'
},
}

Claro que  o cabeçalho já existe devido à declaração das outras variáveis. Coloquei ele no código acima apenas para nos localizarmos onde fazer a alteração no código existente.

Nesse código há algumas definições interessantes:
O nome da propriedade está definido entre aspas simples no começo de cada bloco
isBindingTarget - define se a propriedade pode ser associada a alguma variável na IDE
defaultValue - qual será o valor default para a propriedade
baseType -  tipo da propriedade. Isso definirá qual tipo de variável poderá ser associado na IDE. Note que para uma matriz de valores o ThingWorx usa o tipo INFOTABLE
selectOptions - define quais as opções disponíveis para seleção na IDE. Os valores devem ser passados no formato JSON

Depois que o arquivo é alterado e atualizado no Thingworx, as propriedades do Widget Google ficam conforme a figura ao lado.
Dá pra notar que a variável Boolean ficou como um checkBox, a INFOTABLE ficou com o ícone de uma tabela e a variável String que dependia de uma lista ficou com as opções disponíveis para serem selecionadas.

Agora, com as propriedades já definidas no Widget, e como a propriedade isBindingTarget for definida como True,  é possível associar valores ou um conjunto de valores a elas. No exemplo abaixo, o resultado do serviço QueryDataTableEntries está sendo associado à propriedade CustomLayerData. Esses dados serão utilizados posteriormente quando em Runtime, para que o mapa de calor seja populado.


É isso pessoal, a parte de alteração do Widget para a IDE e configuração da aplicação está OK. Agora falta a parte de Runtime, que irei descrever no próximo post

Cheers
Ewerton




Usando a API do Google Maps para criar um Mapa de Calor: Usando a Função HeatMap (HelloWorld)

Fala Pessoal, tudo bem?

Como mencionei no post anterior eu decidi usar a função Heatmaps layers do Google Maps para servir como base para a criação de dashboards para o projeto SmartCitzen do Hacker Clube de São José dos Campos.

Minha maior preocupação na verdade é um problema bom para se resolver: como a API tem uma cota diária, se a aplicação foi muito utilizada há uma chance de exceder-se a cota. Não vou me preocupar com isso por enquanto.

Exemplo de uso da função HeatMaps Layer

No exemplo que peguei no Google, os dados são 'hardcoded'. Funcionou bem, mas como disse em meu post anterior, a intenção é que os dados sejam dinâmicos, portanto preciso de uma rotina para atualizá-los:

var heatmapData = [
{location: new google.maps.LatLng(37.782, -122.447), weight: 0.5},
new google.maps.LatLng(37.782, -122.445),
new google.maps.LatLng(37.752986, -122.403112),
new google.maps.LatLng(37.751266, -122.403355),
{location: new google.maps.LatLng(37.782, -122.443), weight: 2},
new google.maps.LatLng(37.782, -122.437),
{location: new google.maps.LatLng(37.782, -122.435), weight: 0.5},
{location: new google.maps.LatLng(37.785, -122.447), weight: 3},
{location: new google.maps.LatLng(37.785, -122.435), weight: 3}
];

var heatMapDissipating = this.getProperty('heatMapDissipating');
var heatmap = new google.maps.visualization.HeatmapLayer({


var heatMapGradient = this.getProperty('heatMapGradient');
var heatMapOpacity= this.getProperty('heatMapOpacity')
var heatMapRadius=this.getProperty('heatMapRadius')

data: heatmapData,
dissipating: heatMapDissipating,
opacity: heatMapOpacity,
radius: heatMapRadius
});
heatmap.setMap(this.map);  

O primeiro bloco são dos dados que serão plotados no mapa, o segundo são opções para configurar o mapa e o terceiro é a chamada propriamente dita. Para fazer os dados serem dinâmicos preciso fazer com que a variável heatmapData seja alimentada por dados dinâmicos.

Trabalhando com Widgets:

A plataforma que estou usando, o Thingworx, usa o conceito de Widgets: existe uma interface RAD (Rapid Application Development) que possui elementos pré criados para que possamos criar as interfaces de usuário usando pouco código e o  conceito de drag n' drop (olhem esse video para ter uma ideia)

O Thingworx já possui uma extensão para o Google Maps, porém essa extensão não usa a API do HeatMaps. Decidi usar essa extensão como base e adicionar a funcionalidade. Para fazer isso é necessário alterar 2 arquivos da extensão:

  1. googlemap.ide - possui as características do Widget, propriedades, etc - Será necessário alterá-lo para que a IDE no Thinworx possua as propriedades relacionadas à nova funcionalidade
  2. googlemap.runtime - define as funções e chamadas quando a aplicação está em execução - É nesse arquivo que criaremos as funções JavaScript para tratar chamar a função HeatmapLayer

Estou tentando fazer posts pequenos para que não sejam cansativos, então chega por aqui. Nos próximos post irei mostrar como alterei o arquivo de ide e o de runtime

Cheers
Ewerton






Saturday, December 12, 2015

Pausa no Hardware - Usando a API do Google Maps para criar um Mapa de Calor: Qual escolher?

Fala Pessoal,

Agora que fiquei em paz com o Intel Edison e estou começando uma pequena briga com o Raspberry Pi, vou dar uma pausa no hardware e focar um pouco no desenvolvimento de aplicações.

Como falei no post anterior, estou ajudando o pessoal do Hacker Clube de SJC a criar uma aplicação chamada SmartCitzen, que terá como objetivo criar uma plataforma para que makers, escolas e afins possam enviar dados coletados por seus dispositivos e depois ter uma visão consolidada, rodar ferramentas de análise, etc.

Para isso estamos usando o ThingWorx, plataforma de IoT da PTC. Entre os vários motivos para usar a plataforma, eu trabalho na PTC e sou um entusiasta da tecnologia - curioso - como alguns dizem! Como não tenho background como programador, o ThingWorx é uma mão na roda para acelerar o desenvolvimento e abstrair várias necessidades que não conheço muito - desenvolver em HTML, segurança dos dados, persistir os dados, etc.

Em outro post falo mais sobre ThingWorx!


Hoje vai ser sobre outra surra: usar a API do Google Maps para criar um HeatMap ou mapa de calor (?) para o SmartCitzen. A ideia é consolidar os dados como feito aqui:

O ThingWorx já possui uma extensão para o Google Maps mas essa função não está disponível na extensão provida pela PTC.

USANDO AS APIs

As APIs do Google Maps  possuem ao menos 2 formas para criar um Mapa de Calor

  1. Fusion Tables - é um produto experimental do Google que facilita esse tipo de trabalho. A grande vantagem é o HeatMap ser rodado no servidor e não no cliente, portanto a performance para um alto volume de dados será melhor. Como desvantagem, o fato de que os dados tenham que estar em uma Fusion Table no Google, ou seja, os dados tem que sair da plataforma (sic) e alterá-los dinâmicamente é difícil.
  2. Função Heatmap Layer - mais flexível, porém tem cotas para utilização e cãlculo do layer no cliente.
Vejam uma tabela de comparação retirada da documentação:

Heatmap Layer
Fusion Table Layer
A large number of data points may result in reduced performance.
More data points will have little impact on performance.
Able to customize the appearance of the heatmap by changing such options as: the color gradient, the radius of data points, and the intensity of each data point.
No ability to customize the appearance of the heatmap.
Able to control whether heatmap data dissipates at higher zoom levels or not.
All heatmap data will dissipate as you zoom in.
Data can be stored with your HTML, stored on a server, or calculated on the fly. Data can be changed at runtime.
All data must be stored in a Fusion Table. Data cannot be easily changed at runtime.

Decidi escolher a segunda opção para evitar ter que colocar os dados na Fusion Table. Na verdade vou colocar as 2 opções na extensão, mas meu foco será na Função Heatmaps.

No próximo post irei descrever meu "Hello World" usando a função HeatMaps.







Friday, December 11, 2015

Intel Edison - Trégua por enquanto, paz ainda longe!

Fala Pessoal,

Depois de uma briga enorme com o Edison para fazer o WiFi funcionar e também instalar algumas aplicações para rodar um agente Thingworx (mais sobre isso em outro post), consegui um pouco de paz!

Depois de pegar o jeito de fazer um flash no Edison e ter 99% de sucesso em fazê-lo, entrei em uma outra batalha para instalar o Node.js no Edison.

Fato era que a versão 0.8.1 da lib mraa que é instalada usando o npm  é incompatível com o node v0.12.7. Depois de quebrar a cabeça procurando uma forma de instalar uma versão anterior da lib, descobri que é relativamente simples:

root@edison:~# npm install mraa@0.8.0

irá instalar a versão anterior do node. js
Agora WiFi funcionando, node.js rodando com o mraa irei fazer o ThingWorx funcionar com o Edison. Já fiz alguns testes e estou tendo problemas com a tabela de pinos, então espero mais uma batalha.

Se der muito trabalho vou deixar o Edison de lado e focar no Raspberry Pi, boas ideias e um pessoal animado para um hackathon aqui em SJC!

Hoje foi rápido. Em breve irei fazer um post sobre o ThingWorx e começarei a postar regularmente sobre isso. Estamos usando ThingWorx para criar uma aplicação chamada SmartCitzen.

Keep it up!
Ewerton



Monday, November 30, 2015

Intel Edison Flashing - Nada é tão simples que não possa ser complicado

Fala galera, blz?

Decidi fazer um reflash no Edison porque preciso rodar o node.js e como não manjo tanto dessas coisas preferi seguir a dica do pessoal que já colocou o node.js para rodar.

Me passaram esse procedimento para fazer o reflash. Simples não? Não!

Pra começar, o Edison não apareceu como um drive no meu laptop. Preferi então tentar um procedimento via serial que encontrei aqui.

Maio problema foi que nem o filezilla se conectava no Edison, isso pq venho usando-o a muito tempo. Achei esse procedimento aqui, que aliado a um reboot fez com que eu conseguisse me conectar.

O meu maior problema porém não foi esse, e sim um comportamento estranho do Edison: ao tentar fazer  o flashing usando a Phone Flash tool, que é a ferramenta recomendada, o status ficava em 69%, algumas vezes em 76% e falhava depois de um tempo.

Liguei a conexão serial para acompanhar e percebi que o Edison dava um reboot normalmente, pedia login e senha e se "lixava"  para o processo de flashing. Aí encontrei esse procedimento. E depois de ler tudo com cuidado percebi o procedimento.

Basicamente é necessário deixar o Edison dar o reboot após o 69% e dar um reset (via linha de comando ou via pushbutton), e rodar:

boot> run do_flash

Nessa hora a ferramenta de flashing reconhece o Edison e volta para o processo normal de boot..

Uma coisa que percebi: é necessário deixar o Edison finalizar o boot após o 69%. Tentei interromper e fazer o procedimento mas isso falhou em todas as vezes. Já deixando o boot ocorrer normal e resetando eu tive sucesso em 99% das vezes. Quando falhou eu fechei a Flash Lite Tool e reiniciei o Edison. E deu tudo certo.. Então, o passo a passo que segui foi esse.


  1. Ligar o Edison e conectar-se à serial utilizando o Putty ou uma ferramenta similar;
  2. Desligar o Edison e preparar o Flash Lite tool para o procedimento de Flash;
  3. Abrir o diretório da imagem a ser carregada e procurar o arquivo FlashEdison.json 
  4. Certificar-se que em Configuration a selção seja RNDIS (para Windows)
  5. Clicar em Start Flash
  6. Religar o Edison e aguardar até 69%;
  7. Quando o Edison der o boot, caso a Flash Lite tool não reconhecer, esperar o boot completar, logar no Edison e dar o comando reboot;
  8. Monitorar o boot pela serial e quando a msg "Hit any key to stop autoboot" , pressione uma tecla. Aparecerá o prompt de comand boot>;
  9. Dar o comando run do_flash
  10. A ferramenta de flash deve continuar e finalizar o procedimento;
Espero que funcione para vocês. Para mim funcionou bem.

Abraços

Keep it up!
Ewerton



Wednesday, November 25, 2015

Intel Edison e Wifi - a grande batalha - Capítulo 2




Fala Pessoal! Eu por aqui de novo!

Continuando minha batalha com o Edison para fazer o wifi funcionar.

Acabei de perceber que agora ao dar um ifconfig só aparecem as porta wlan0 e a lo, e antes tinha a porta usb0, o que me leva a crer que pode haver algum tipo de conflito que faz com que a wlan0 pare de funcionar.

Não fiz nada em especial para que o problema fosse resolvido, mas me parece que esse post também descreve o problema, mas nesse caso houve uma ação específica para desabilitar a usb0, o que eu não fiz.

Vamos esperar, não vou dar essa batalha como vencida porque acho que ainda haverão outras, mas o problema não se repetiu novamente.

PS: Interessante o post sobre o Edison no Embarcados

Keep it up!
Ewerton

Tuesday, November 24, 2015

Intel Edison e Wifi - a grande batalha - capítulo 1

Fala Pessoal!

Resolvi seguir o conselho do grande Mauro Assis, do AutomatoBR e registrar minhas aventuras no mundo das coisas.


Isso deve ajudar outros que como eu ficam perdidos nesse mar de informação que é a internet e que muitas vezes não leva a uma conclusão.


Pois é: decidi começar fazendo uma conexão simples do Edison com minha instância ThingWorx para o projeto SmartCitzen do Hacker Clube de SJC


Para minha surpresa, ao contrário do que havia lido, O Edison é um touro bravo! Difícil para quem está iniciando, por conta dos imensos problemas que apresenta.


O básico do básico - como por exemplo, a conexão wifi - que no meu Raspberry Pi funcionou tão rapidamente (com ajuda das dicas do AutomatoBR), no Edison é super instável!


Algumas coisas que aconteceram (aleatoriamente, sem muita sequência lógica):

  1. O comando configure_edison --setup não acha nenhuma rede
  2. O comando configure_edison --setup acha uma rede porém não se conecta a ela. Erro: Attempting to enable network access, please check 'wpa_cli status' after a minute to confirm.
    Not connected. Something went wrong
  3. O comando ifconfig não acha a wlan0
  4. O comando iconfig wlan0 up sobe a wlan0 mas ela some em seguida ao dar o comando ifconfig 
Encontrei muitas referencias aos problemas no site da intel, com as mais diversas recomendações e até agora nada resolveu.

De repente eu dei o comando ifconfig wlan0 up e depois  ifconfig -a e a rede se estabeleceu. Vamos ver se estabiliza.

Vou fazer mais uns testes e posto os resultados (capítulo 2 aqui)  , mas é provável que eu tenha que fazer um reflash no Edison por causa de um problema com o node.js, mas isso é também assunto para outro post.

Keep it up!
Ewerton