• No results found

Høringsuttalelser

In document Melding for året 2013 (sider 33-0)

IV. Oversikt over saker i 2013

6. Høringsuttalelser

A classe RestRequest (Figura 9) contém métodos de apoio para o parsing do pedido, disponibilizando um conjunto de variáveis e atributos para serem utilizados pelas classes de serviço. As classes de serviço derivam desta classe. O construtor é responsável por receber a informação do pedido, diretamente da “main function” da aplicação FastCGI (ver secção 2.7.2), e guardá-la nos atributos respetivos. As classes de serviço não precisam de fazer mais nenhuma inicialização além deste processamento comum.

Figura 9 - Classe RestRequest

Para cada pedido o construtor recebe como parâmetros de entrada respetivamente: o verbo HTTP (GET, POST, PUT, DELETE), o URI, a querystring, o login e password de acesso ao dispositivo (HTTP_USR e HTTP_PWD) e o conteúdo do body (será diferente de string vazia caso o pedido seja PUT ou POST).

Após receber essa informação, nos seus parâmetros de entrada, irá guardar a informação de contexto em atributos protected, assegurando deste modo o acesso aos mesmos pelas classes de serviço. A informação guardada no seu contexto é fundamentalmente constituída por:

43

login e password de acesso à câmara;

querystring do pedido (request_uri);

 verbo HTTP do pedido (request_method);

entidade/token a que o pedido se destina (entity):

o contém o recurso ou o token relativo ao 1º segmento do pedido imediatamente a seguir ao segmento que identifica o serviço ONVIF:

/onvif/{serviço onvif}, {endereço_serviço_codificado}/{entidade/token}

e.g.: users, profiles, streamURI, token do perfil de media, etc.

 segmentos do URI do pedido, uma vez que os segmentos do pedido definem o recurso REST;

objeto JSON com o conteúdo body do pedido caso se aplique;

informação se o parsing do conteúdo do body para JSON ocorreu com sucesso (contenteok);

descrição do erro, no caso ocorrer um erro de parsing do conteúdo do body para JSON (contenterror).

Ao nível do SW REST, e relativamente ao 1º segmento do pedido imediatamente a seguir ao segmento que identifica o serviço ONVIF, temos situações em que este é um recurso e outras situações em que se trata de um token.

Exemplificando para a 1ª situação (pedido de edição de um dado perfil media):

Exemplificando para a 2ª situação (pedido de consulta dos presets de um dado perfil de

media):

Adicionalmente, em todos os segmentos dos URIs do serviço REST existe uma alternância entre segmentos do “tipo” recurso e segmentos do “tipo” token, o que nos leva a ter os seguintes padrões de URI:

/onvif/{serviço},{endereco_servico_codificado}/{recurso}/{token}/{recurso}/{token}

ou

PUT /onvif/media,{endereco_servico_codificado}/profiles/{mediaProfile}

44

/onvif/{serviço},{endereco_servico_codificado}/{token}/{recurso}/{token}/{recurso}

Ao nível dos métodos disponibilizados pela classe RestRequest às classes de serviço destacam-se 3 grandes grupos:

 tratamento de mensagens de erro e de sucesso:

o getLastError – obtém a informação sobre erros ONVIF em formato JSON; o msgFormat – obtém mensagem de sucesso ou erro (todos os erros que não

são ONVIF, e.g: URI Inválido) em formato JSON;

obtenção dos vários segmentos do URI do pedido e da respetiva querystring:

o getResourcesSegments – dado o URI do pedido, obtém o primeiro segmento após o segmento que define o endereço do serviço na câmara. Ou seja, inicializa um apontador para o primeiro segmento encontrado nestas condições (primeiro recurso);

o getCurrentResourcesSegment – devolve o segmento atual (o recurso atual), mantendo o apontador;

o getNextResourcesSegment – incrementa o apontador e devolve o próximo segmento (próximo recurso);

o getQueryParameter – obtém da querystring o valor para a chave dada;

 e por último, apoio na obtenção e tratamento do URI do pedido:

o UriEncode – dada uma string devolve a mesma codificada para que possa ser utilizada num URI;

o getServiceURL – devolve o segmento do URI que define o endereço do serviço na câmara do pedido;

o UriWithoutQS - devolve todo o URI do pedido sem a querystring, caso exista.

Estes métodos são muito utilizados nas classes de serviço para o processamento do pedido e respetivo direcionamento para o método respetivo da biblioteca UMOC.

Em caso de erro, deve ser assegurada a sua devolução no ciclo de vida de um dado pedido. Cada função da biblioteca UMOC pode devolver erro, e esse erro só diz respeito à última

45

função invocada. De modo a garantir a passagem da informação de erro ONVIF para o serviço REST (que serve vários clientes), devem ser logo invocadas as funções da biblioteca UMOC de obtenção de informação do erro. Desta forma deverá ser executado o método “getLastError” da classe RestRequest do serviço REST, que por sua vez chama os métodos “getError” e “getErrorDetail” fornecidos pela biblioteca UMOC [20]. O primeiro devolve a informação do erro, no caso de ser um erro ONVIF, enquanto o segundo devolve o detalhe do erro, caso esse mesmo detalhe exista (ver Figura 10).

Figura 10 – Tratamento do erro dum pedido mal sucedido ao SW REST

Esta informação é de seguida devolvida, em formato JSON, como resultado do pedido. Desta forma, estamos a evitar a necessidade de manutenção de estado ao nível do serviço web, o que violaria o princípio REST da não manutenção do estado no servidor.

Nesta classe, é ainda disponibilizado um conjunto de constantes estáticas que contêm fundamentalmente a definição de mensagens de erro e de sucesso, para serem utilizadas nas classes de serviço.

4.7.3 Classes de serviço

Uma vez que as classes de serviço têm todas uma estrutura e modo de funcionamento idênticas, sendo diferenciadas apenas nos métodos privados que as constituem (e que são responsáveis por mapear e invocar os métodos da biblioteca UMOC), descreve-se apenas uma delas: a classe de serviço rest_ptz (Figura 11).

46

Figura 11 - Classe de serviço rest_ptz

Cada classe de serviço ONVIF tem definido um enumerado (ver Figura 6) cujo objetivo é permitir indexar os respetivos recursos. Tem também uma estrutura unordered_map que mapeia num valor do enumerado as strings possíveis de um segmento do URI. Por exemplo, na classe rest_ptz, o mapa de recursos s_mapEntitiesPtz mapeia a string “/presets” no valor inteiro ptz_presets (Anexo F – Detalhe 1). Este tipo de mapeamento é feito para os vários segmentos do URI e permite utilizar a instrução switch para encaminhar o pedido até à função respetiva da biblioteca UMOC. Quem faz este trabalho são métodos privados da classe de serviço, no exemplo da Figura 11, getPtzStatus, getPresets, createPresets, deletePreset, gotoPreset, putHomePosition, putMoveStop. Mapeamentos idênticos encontram-se definidos para os restantes serviços (media, devicemgmt, imaging, etc). Optou-se pela utilização de unorderd_map em detrimento de map [40], pois a primeira é mais eficiente que a segunda quando a ordem dos elementos não é importante, como é o caso. A alternativa a este mecanismo de indexação (um hashing + comparação de inteiros) seria trabalhar com strings. No caso do PtzEntities isso significaria que potencialmente poderiam ocorrer 4 comparações de strings para seguir para o próximo segmento. Noutras classes de serviço o número de comparações possíveis ascende a 11 (rest_devicemgmt). O mecanismo implementado garante que no máximo é processada uma string (hashing) e que o número variável de comparações é feito sobre inteiros. Este facto traduz-se num ganho de desempenho do servidor.

A construção dos mapas de recursos é realizada por métodos estáticos existentes em todas as classes de serviço, denominados inicializaRecursos. A inicialização destes mapas é realizada fora do ciclo da aplicação FastCGI (secção 2.7.1).

47

Após o método getRestResult ser invocado (Figura 7), e considerando o caso de se pretender obter os presets de um dado perfil de media:

temos a seguinte sequência (Figura 12):

Figura 12 - Mapeia função UMOC

 obtém o recurso do primeiro segmento (após o segmento que define o endereço do serviço na câmara) utilizando o mapa de recursos. No exemplo este segmento é o

token do perfil de media a que o pedido se refere.

 executa os métodos “getCurrentResourcesSegment” e “getNextResourcesSegment” da classe RestRequest, obtendo respetivamente a informação do token do perfil media ({mediaProfile}) e do recurso (presets);

 com a informação do verbo HTTP do pedido (GET) e do recurso (presets) é selecionado e invocado o método getPresets;

 este executa o método getServiceURL para obter o segmento do URI que define o endereço do serviço na câmara do pedido;

 e invoca a função da biblioteca UMOC relativa ao pedido (e.g., get_Presets_LL) juntando à informação obtida a informação da login e password de acesso à câmara;

 recebe o resultado, e devolve-o ao método “getRestResult” (Figura 13):

48

Figura 13 - Trata informação devolvida

Todos os pedidos efetuados ao serviço REST obedecem a esta sequência diferindo apenas: 1. no método privado da classe de serviço a ser executado;

2. na função da biblioteca UMOC a ser executada pelo método privado;

3. nos parâmetros obtidos e passados aos métodos privados (e por consequência aos métodos da biblioteca UMOC);

4. na informação devolvida na resposta pois, apesar da resposta ser sempre um objeto JSON, a sua estrutura e o seu conteúdo variam consoante o pedido.

Os dados que são processados dependem do tipo de pedido HTTP, nomeadamente se têm ou não corpo e querystring. No caso de chamadas ao serviço REST em que existe informação no corpo do pedido (e.g., verbos PUT e POST), os métodos privados de encaminhamento obtém-na recorrendo às propriedades (protegidas) da classe RestRequest denominadas “jsoncontent”, “contentok” e ”contenterror”. A primeira é um objeto JSON com o conteúdo do corpo, a segunda indica se o parsing do conteúdo para JSON ocorreu com sucesso e a terceira contém o erro (caso exista) que ocorreu no parsing para JSON (Anexo F – Detalhe 2).

No caso de existirem parâmetros ao nível da querystring do pedido, os mesmos são obtidos efetuando uma chamada ao método “getQueryParameter” da classe RestRequest (Anexo F – Detalhe 3).

Os valores obtidos serão posterimente utilizados na invocação da função da biblioteca UMOC.

Resumindo, todas as classes de serviço apresentam uma estrutura e modo de funcionamento idênticas em que todos os pedidos efetuados obedecem a uma mesma

49

sequência de execução, diferindo apenas no método da biblioteca UMOC invocado, na obtenção dos parâmetros e no resultado devolvido.

51

5 Cliente AJAX

A aplicação cliente está estruturada em duas camadas: uma biblioteca JavaScript que abstrai a comunicação com o serviço web REST e a camada que trata da interface gráfica com o utilizador.

5.1 Biblioteca JavaScript

A biblioteca JavaScript implementada fornece uma API cliente de comunicação com o serviço web REST. Esta API é constituída por um objeto câmara (objCamera) e objetos de serviço (objDeviceMngm, objMedia, objPTZ, objImaging, objDeviceIO e objEvents) correspondentes aos serviços ONVIF (Figura 14).

Figura 14 - Objetos da biblioteca JavaScript

Nome Descrição

objDeviceMngm Disponibiliza as funções necessárias à gestão de utilizadores, configurações de rede, configuração de segurança e configuração de sistema (capacidades do dispositivo, reset, reboot, restore, etc .

objMedia Disponibiliza as funções necessárias à configuração dos perfis de media e de propriedades de streaming de um dispositivo.

objPtz Disponibiliza as funções que possibilitam os movimentos Pan, Tilt and Zoom (PTZ) de um dispositivo.

objDeviceIO Disponibiliza as funções que permitem obter e configurar as entradas e saídas físicas de um dispositivo.

objImaging Disponibiliza as funções que permitem controlar e configurar as propriedades de imagem de um dispositivo.

objEvents Disponibiliza as funções relativas à subscrição e obtenção de notificações de eventos.

Tabela 2 - Objetos de serviço

O objeto câmara serve essencialmente para passar informação de contexto aos objetos de serviço (e.g: ip:port, login e password de acesso ao dispositivo selecionado), não existindo uma relação de herança (prototípica) entre estes objetos e o objeto de câmara.

Os objetos de serviço são os responsáveis por efetuar as chamadas ao serviço web REST e recebem, no seu construtor, um objeto câmara como parâmetro.

52

5.1.1 Objeto câmara

Para um dado dispositivo, o objeto câmara:

 guarda informação que seja necessária partilhar entre os vários objetos de serviço (ip:porta, login e password de acesso);

 guarda informação de estado relativa à câmara selecionada;

 fornece métodos de apoio utilizados pelos diversos serviços, evitando deste modo a duplicação de código pelos diversos objetos de serviço.

5.1.1.1 API

De forma a que seja possivel guardar informação de contexto, no desenvolvimento do objeto câmara, foi adotado o modelo híbrido constructor/prototype (secção 2.3).

Este objeto disponibiliza um conjunto de funções prototype, que utilizam as propriedades

capabilities e o endereço do dispositivo de cada objeto para obter os endereços de acesso

para os vários serviços ONVIF (media, imaging, PTZ, etc) do dispositivo correspondente. Quando é criado um objecto câmara são obtidas as capabilities da respetiva câmara. No entanto, algumas câmaras (com implementações ONVIF deficientes) devolvem endereços de serviço que são IPs internos, impossibilitando a sua utilização. Por isso, foi criado o método estático getValidURL que tem como objetivo garantir que o IP utilizado é o IP externo da câmara. Este método é utilizado por um conjunto de funções prototype que fornecem os endereços dos vários serviços ONVIF (media, imaging, PTZ, etc). As funções da API (i.e., utilizadas pela camada de interface gráfica) disponibilizadas pelo objeto câmara são as apresentadas Tabela 3:

Nome Descrição

getValidURL (estática)

Dado um URL e o IP externo (IP:Porta) de acesso à câmara substitui qualquer IP que conste no URL pelo IP externo fornecido.

getFirstProfile (estática)

Dado um array de perfis media devolve o primeiro que contenha um video source e um video encoder.

mediaXAddr imagingXAddr ptzXAddr

O objeto câmara irá conter 3 funções que, dado o objeto capabilities e o endereço do dispositivo, irão devolver o endereço de acesso ao respetivo serviço ONVIF.

Tabela 3 - Funções disponibilizadas pela API do objeto câmara

53

Nome Descrição

CameraIPPort IP e porta (formato IP:Port) de acesso do dispositivo. login Utilizador a ser usado no acesso ao dispositivo. password Palavra-chave utilizada no acesso ao dispositivo.

capabilities Objeto JSON com os detalhes das capabilities do dispositivo (ver secção 2.4). snapshotimage URL de acesso a um snapshoot/imagem do dispositivo.

id Identificador do dispositivo na base de dados. Tabela 4 - Propriedades do objeto de câmara

5.1.1.2 Métodos de Apoio

O objeto câmara implementa um conjunto de métodos de apoio, apresentados na Tabela 5, que são utilizados pelos diversos objetos de serviço:

Nome Descrição

createXHR (estática)

Cria e devolve um objeto XMLHttpRequest. XHRHeaders

(estática)

Recebe como argumentos o objeto XMLHttpRequest, o username, a password e o tamanho do corpo do pedido, preenchendo o header do primeiro (ver secções 4.3 e 4.5).

getRequestResult (estática)

Recebe como argumentos o objeto XMLHttpRequest e uma função de callback específica.

getURIByMethodAndTitle (estática)

Recebe como argumentos um objeto do tipo resources_uri, método HTTP (GET, POST, PUT e DELETE) e o título da operação pretendida devolvendo o URI para o recurso REST pretendido.

Tabela 5 - Funções internas do objeto câmara

A função getRequestResult tem um papel decisivo na forma como a biblioteca suporta tanto pedidos síncronos como assíncronos. Ela é invocada pelo objeto XMLHttpRequest, invocando de seguida a callback específica. No caso do pedido ser síncrono, getRequestResult é invocada diretamente pelo objeto de serviço, recebendo neste caso uma

callback específica nula e devolvendo a resposta do pedido HTTP (já terminado).

Ou seja, conforme descrito na Figura 15, no caso da função da callback ser nula, getRequestResult considera que o pedido foi síncrono e devolve o objeto JSON. Caso contrário, considera que o pedido foi assíncrono, passando o resultado à função de callback recebida.

54

Figura 15 - Execução da função getRequestResult

A função getURIByMethodAndTitle permite obter URIs a partir de uma lista de recursos (resources_uri). Além de unificar este tipo de código, permite que a biblioteca seja independente da forma como o serviço web REST estrutura e codifica os URI de acesso os recursos. Por exemplo, se dado o objeto resources_uri:

for passado como argumento na invocação:

obtem-se como resultado:

Assim, esta função possibilita que, se por alguma razão no futuro os URI de acesso aos recursos forem alterados no serviço web REST, a biblioteca continua a ser capaz de obter os novos URIs sem necessidade de fazer alterações à sua codificação.

{

"resources_uri": [ {

"method": "GET", "rel": "_self",

"title": "Device Management",

"uri": "/onvif/devicemgmt,208.124.204.172%3A81%2Fonvif%2Fdevice_service/" },

{

"method": "GET",

"rel": "/onvif/devicemgmt,208.124.204.172%3A81%2Fonvif%2Fdevice_service/", "title": "Device Information",

"uri":"/onvif/devicemgmt,208.124.204.172%3A81%2Fonvif%2Fdevice_service/DeviceInformation" }

]}

objCamera.getURIByMethodAndTitle(this.resources.resources_uri, 'GET', 'Device Information')

55

5.1.2 Objetos de serviço

Os objetos de serviço são os responsáveis por efetuar as chamadas REST ao serviço web. Cada operação (sobre um recurso) de um serviço REST é mapeada numa função de um objeto de serviço, e por isso não carecem de descrição. Algumas das funções implementadas em cada objecto estão listadas na Figura 16:

Figura 16 - Funções dos objetos de serviço

Todas as funções dos objetos de serviço têm como argumento uma função de callback específica. No caso da função de callback ser nula será considerado que o pedido é síncrono sendo o resultado devolvido diretamente. Caso contrário, considera que o pedido foi assíncrono, devolvendo o resultado ao cliente invocando a função de callback.

Para além da função de callback, podem existir argumentos adicionais nas funções disponibilizadas pelos objetos de serviço. É possível identificar a existência de um padrão, por tipo de operação, na definição desses argumentos:

 operações de criação (POST) - um argumento com a representação JSON da entidade a ser criada. Exemplificando para o caso da criação de um utilizador:

onde “data“ deverá conter o objeto JSON do utilizador a criar.

 operações de edição (PUT) – um argumento com a representação JSON da entidade a ser alterada. Exemplificando para o caso da edição de um utilizador:

objDeviceMngm.prototype.updateUser = function (data,callback) { //código da função

}

onde “data“ deverá conter o objeto JSON do utilizador a editar.

objDeviceMngm.prototype.createUser = function (data,callback) { //código da função

56

 operações de remoção (DELETE) – um argumento com o identificador do recurso a ser removido. Exemplificando para o caso da remoção de um utilizador:

objDeviceMngm.prototype.deleteUser = function (username,callback) { //código da função }

onde “username“ deverá conter o login do utilizador a remover.

 operações de obtenção de um dado recurso (GET) – um argumento com o identificador do recurso. Exemplificando para o caso da obtenção do URI de

snapshot de um dado perfil de media (token):

objMedia.prototype.getSnapshotURI = function (token, callback) { //código da função

}

No desenvolvimento dos objetos de serviço foi adotado o modelo híbrido

constructor/prototype (secção 2.3). Assim, as funções dos objetos de serviço são

implementadas utilizando o prototype do JavaScript, assegurando deste modo a partilhada destas funções por todas as instâncias de um dado objeto.

No Anexo G – Detalhe 1 encontra-se o detalhe de implementação do construtor dos objetos de serviço. No Anexo G – Detalhe 2 encontra-se, a título de exemplo, a implementação da função para obter o “Device Information” de um dispositivo.

Todas as funções de um objeto de serviço seguem uma sequência idêntica (ver Figura 17): 1. O cliente web cria um objeto câmara;

2. cria o objeto de serviço indicado para o pedido, passando ao seu construtor o objeto câmara;

3. obtém do serviço REST (invocação síncrona) os resources_uri válidos para o serviço em causa;

4. o cliente web invoca a função pretendida do objeto de serviço (API_method): a. caso se pretenda efetuar uma chamada assíncrona deve receber como

argumento uma função de callback que será executada no retorno da informação do pedido.

57

b. caso se pretenda uma chamada síncrona deve receber uma função de callback nula.

5. invoca a função createXHR do objeto câmara (secção 5.1.1.2); 6. invoca a função getURIByMethodAndTitle (secção 5.1.1.2); 7. invoca a função XHRHeaders (secção 5.1.1.2);

8. envia o pedido HTTP ao serviço web REST;

9. após o envio do pedido pelo objeto XMLHttpRequest invoca a função getRequestResult:

a. caso o pedido seja assíncrono, ela é invocada pelo objeto XMLHttpRequest, invocando de seguida a callback específica (ou da camada GUI) e passando- lhe o resultado (objeto JSON);

b. caso o pedido seja síncrono, getRequestResult é invocada diretamente, com

callback específica nula no argumento, devolvendo como resposta do

pedido HTTP (já terminado) o objeto JSON retornado.

10. em pedidos subsequentes ao mesmo objeto de serviço os passos 1,2 e 3 não serão necessários.

58

Figura 17 - Sequência de um pedido à biblioteca JavaScript

As propriedades disponibilizadas pelos objetos de serviço são as apresentadas na Tabela 6:

Nome Descrição

ip_port IP e porta (formato IP:Port) do dispositivo (obtido do objeto câmara). Aplicável apenas no objeto objDeviceMngm.

59

Nome Descrição

url URL de acesso ao serviço. Aplicável a todos os objetos com a exceção do objeto objDeviceMngm.

login Utilizador a ser usado no acesso do dispositivo (obtido do objeto câmara). password Palavra-chave utilizada no acesso ao dispositivo (obtida do objeto câmara). resources Objeto JSON com os URI (resources_uri) válidos para o serviço em causa. Tabela 6 - Propriedades dos objetos de serviço

In document Melding for året 2013 (sider 33-0)