�
UNIVERSIDADE FEDERAL DE SANTA CATARINA DEPARTAMENTO DE INFORMÁTICA E ESTATÍSTICA
CURSO DE SISTEMAS DE INFORMAÇÃO
KARINE PETRY PAULA MARIEN ALBRECHT LOPES
MODELOS PARA INTEROPERABILIDADE DE SISTEMAS
HOSPITALARES UTILIZANDO PADRÃO HL7
Trabalho de Conclusão de Curso apresentado como parte dos requisitos para obtenção
do grau de Bacharel em Sistemas de Informação
FLORIANÓPOLIS, 2005/2
Karine Petry
Paula Marien Albrecht Lopes
MODELOS PARA INTEROPERABILIDADE DE SISTEMAS
HOSPITALARES UTILIZANDO PADRÃO HL7
Trabalho de Conclusão de Curso apresentado como parte dos
requisitos para obtenção do grau de bacharel em
Sistemas de Informação
Orientador: Prof. Dr. Rer. Nat. Aldo Von Wangenheim
Co-orientador: Bel. Rafael Simon Maia
Banca Examinadora: Me. Rafael Andrade
Bel.Thiago Ramos dos Santos
III
AGRADECIMENTOS
Aos nossos pais pelo incentivo e apoio durante nossas vidas.
Pelos quitutes oferecidos pela Andrea nos dias cansativos de trabalho.
Pela oportunidade oferecida pelo professor Aldo no desenvolvimento deste projeto.
Ao nosso co-orientador Rafael Simon Maia que apesar de comer nossas bolachas, muito nos
ajudou nos momentos de dificuldade.
A todos os colegas do laboratório de Telemedicina, responsáveis pela nossa necessária perda
de concentração.
Ao Eduardo pela paciência e pelos almoços tardios.
Ao Thiago Machado e Evandro Espindola pela amizade e companheirismo oferecidos durante
todo o curso.
Enfim, agradecemos a todos que de alguma forma contribuíram para este trabalho.
IV
“Bem-vindas ao mundo real!!!”
Rafael Simon Maia, 04/04/2005.
V
RESUMO A aceitação em grande escala da informática proporcionou maior investimento no setor. Em
decorrência disto, houve um aprimoramento tecnológico em hardware e software. Dessa
forma, diversos sistemas de informação foram desenvolvidos. Surge então, o problema da
interoperabilidade- sistemas necessitam se comunicar para permitir o acesso às informações.
Assim, torna-se indispensável o estabelecimento de padrões com o intuito de promover esta
tão esperada interoperabilidade. A área de saúde por manipular com informações de elevada
importância deve receber um tratamento especial, dessa forma, em 1987, um conjunto de
membros de diversos países reuniu-se com o objetivo de elaborar um padrão de caráter
internacional que promova a interoperabilidade de sistemas da área de saúde. Este padrão foi
denominado de HL7. O HL7 é um padrão reconhecido pela ANSI que permite a comunicação
entre sistemas através do envio de mensagens, sendo que sua flexibilidade permite a
interoperabilidade de sistemas independente dos equipamentos, sistemas operacionais,
linguagens e banco de dados utilizados. Explorando ainda mais esta flexibilidade, verificou-se
que é possível o desenvolvimento de um middleware projetado segundo as especificações do
padrão HL7 capaz de facilitar a integração de sistemas legados com os sistemas de última
geração.
Palavras-chave: interoperabilidade, saúde, HL7, flexibilidade, mensagens, middleware, sistemas legados.
VI
Listas de Abreviaturas ABRAMGE – Associação Brasileira de Medicina de Grupo AMB – Associação Médica Brasileira ANSI – American National Standards Institute CID – Código Internacional de Doenças DATASUS – Departamento de Informação e Informática do Sistema Único de Saúde DICOM – Digital Imaging and Communications in Medicine EDIFACT – Electronic Data Interchange For Administration, Commerce and Transport HIPAA – Health Insurance Portability and Accountability Act HIS – Hospital Information System HL7 – Health Level Seven IEEE 1073 – Standard for Medical Device Communications ISO – International Standards Organization LOINC – Logical Observation Identifiers Names and Codes NCDCP – National Council for Prescription Drug Programs OSI – Open System Interconnection PACS – Picture Archive and Communications System PEP – Prontuário Eletrônico de Paciente PRC – Padronização de Registros Clínicos SBIS – Sociedade Brasileira de Informática em Saúde SNOMED - Systematized Nomenclature of Medicine XML – Extensible Markup Language
VII
Listas de Figuras Figura 1 – Modelo ISO / OSI ...................................................................................................21 Figura 2 – O evento "admissão de paciente" origina o envio automático de informação........27 Figura 3 – Formato de uma mensagem HL7 ............................................................................28 Figura 4 – Constituição de uma mensagem de identificação de paciente(A01).......................29 Figura 5 – Mensagem HL7 para admissão de paciente ............................................................29 Figura 6 – Alguns tipos de mensagens .....................................................................................30 Figura 7 – Admissão de Paciente – Mensagem ADT-A01 ......................................................31 Figura 8 – Exemplos de eventos da mensagem ADT...............................................................31 Figura 9 – Segmentos da mensagem de solicitação de informação de paciente ......................31 Figura 10 – Mensagem HL7 para solicitação de informação de paciente................................32 Figura 11 – Segmento resposta Confirmação...........................................................................32 Figura 12 – Mensagem HL7 ACK ...........................................................................................32 Figura 13 – Segmentos da resposta à solicitação de informação de paciente ..........................33 Figura 14 – Mensagem HL7 resposta à solicitação de informação de paciente.......................33 Figura 15 – Mensagem de atualização dados do paciente........................................................33 Figura 16 – Principais segmentos do padrão HL7....................................................................34 Figura 17 – Geração dos analisadores léxico e sintático ..........................................................41 Figura 18 – Tabela da Análise Léxica ......................................................................................45 Figura 19 – Gramática referente ao campo MSH.....................................................................47 Figura 20 – Arquitetura do Modelo Especializado..................................................................48 Figura 21 – Estrutura do servidor (tabelas base de dados) .......................................................48 Figura 22 – Tabela de Configurações do servidor....................................................................49 Figura 23 – Tabela de Caixa de Saída ......................................................................................49 Figura 24 – Tabela de Erros .....................................................................................................49 Figura 25 – Arquitetura do Modelo Genérico ..........................................................................50 Figura 26 – Mensagens Trocadas Entre Aplicação e Cliente...................................................53 Figura 27 – Mensagens Trocadas Entre Cliente x Servidor .....................................................54 Figura 28 – Mensagem de solicitação de dados de paciente ....................................................55 Figura 29 – Mensagem de resposta à solicitação de dados de paciente ..................................55 Figura 30 – Cadastro de Paciente .............................................................................................75 Figura 31 – Recebimento de mensagem...................................................................................75 Figura 32 – Prontuário Eletrônico - Recebimento de Cadastro de Paciente ............................76 Figura 33 – Prontuário Eletrônico - Recebimento de resultado de exames..............................76 Figura 34 – Prontuário Eletrônico - Mensagem de recebimento de cadastro...........................76 Figura 35 – Gerência de Exames - Cadastro Exame ................................................................77 Figura 36 – Gerência Exames - Buscar Paciente......................................................................77 Figura 37 – Gerência Exames - Resultado Exame ...................................................................78 Figura 38 – Gerência Exames - Mensagens Recebidas............................................................78 Figura 39 – Servidor HL7 - Recebimento de mensagem admissão de paciente ......................79 Figura 40 – Cliente HL7...........................................................................................................79
VIII
Sumário
1 INTRODUÇÃO.......................................................................................................................7
1.2 JUSTIFICATIVA .............................................................................................................8 1.3 OBJETIVO .......................................................................................................................9
1.3.1 Objetivo Geral ...........................................................................................................9 1.3.2 Objetivos Específicos ................................................................................................9
1.4 MÉTODO .........................................................................................................................9 2 A INFORMATIZAÇÃO NA ÁREA DE SAÚDE................................................................11
2.1 VANTAGENS DA INFORMATIZAÇÃO NA ÁREA DA SAÚDE ............................11 2.2 INFORMATIZAÇÃO DA SAÚDE NO MUNDO ........................................................12 2.3 INFORMATIZAÇÃO DA SAÚDE NO BRASIL .........................................................13
3 INTEROPERABILIDADE DE SISTEMAS.........................................................................17 3.1 INTEROPERABILIDADE DE SISTEMAS HOSPITALARES ...................................17
3.1.2 Padronização............................................................................................................17 4 TECNOLOGIAS E PADRÕES ............................................................................................20
4.1 HL7.................................................................................................................................20 4.1.1 Princípios Básicos do Padrão HL7 ..........................................................................21 4.1.2 Versões Publicadas ..................................................................................................24 4.1.3 Potencialidades do HL7...........................................................................................26 4.1.4 Utilizações das versões 2.x......................................................................................26 4.1.5 Especificação da Versão 2.3....................................................................................27 4.1.6 Quem utiliza HL7? ..................................................................................................34
4.2 MIDDLEWARE.............................................................................................................35 4.3 PROGRAMAÇÃO PARALELA E DISTRIBUÍDA .....................................................37
4.3.1 C++..........................................................................................................................39 4.4 G.A.L.S...........................................................................................................................40
5 SISTEMAS MODELOS .......................................................................................................43 5.1 GRAMÁTICA................................................................................................................43 5.2 ARQUITETURA............................................................................................................48
5.2.1 Modelo Especializado .............................................................................................48 5.2.2 Modelo Genérico .....................................................................................................49
5.3 FUNCIONALIDADES ..................................................................................................51 5.3.1 Modelo Especializado .............................................................................................51 5.3.2 Modelo Genérico .....................................................................................................52
5.4 MODELO ESPECIALIZADO X MODELO GENÉRICO............................................55 6 CONCLUSÃO.......................................................................................................................57 7 TRABALHOS FUTUROS....................................................................................................58 REFERÊNCIAS .......................................................................................................................59 ANEXO 1 – ANÁLISE DE REQUISITOS .............................................................................62 ANEXO 2 – GRAMÁTICA IMPLEMENTADA....................................................................80 ANEXO 3 – ARTIGO ..............................................................................................................87 ANEXO 4 – CÓDIGO FONTE................................................................................................91
7
1 INTRODUÇÃO
O surgimento da informática pode ser visto como um passo fundamental para se
atingir a era da sabedoria. Com o decorrer dos tempos, e o aprimoramento tecnológico,
tornou-se possível armazenar grandes volumes de dados; em seguida, permitiu-se analisar
informações para gerar conhecimento e, agora estamos em busca da sabedoria usando como
fonte o conhecimento adquirido das nossas informações.
Já que é praticável armazenar informações, é conseqüentemente vital compartilhá-las
com outros sistemas, dessa maneira, diversos protocolos foram desenvolvidos para
proporcionar a comunicação entre os mais variados tipos de sistemas, incluindo dispositivos,
sistemas operacionais, aplicativos e bases de dados. Como os investimentos no setor
tecnológico partem geralmente do meio empresarial, a grande maioria destes mecanismos foi
projetada para a área de negócios. Porém, tornou-se evidente que o ambiente de saúde
também merece uma atenção especial, pois os registros de informações de saúde de um
paciente como laudos, sinais vitais, exames laboratoriais, e imagens médicas, não devem ser
armazenados exclusivamente em papel, e sim, em meio eletrônico, para então proporcionar
uma disponibilidade agilizada, de modo confiável, com integridade garantida, admitindo
redução de custos e ainda com uma independência física do local de armazenamento. Dessa
forma, surgiu o Prontuário Eletrônico de Pacientes (PEP), com a finalidade de permitir a
integração e armazenamento da informação clínica e administrativa de pacientes de forma
individual.
O emprego do prontuário eletrônico trouxe como conseqüência imediata à necessidade
de uma padronização para garantir a transmissão das informações clínicas de um paciente,
entre os mais diversos sistemas de informação que englobam um ambiente de saúde. Assim,
com este propósito, foram projetados alguns padrões, destacando-se o padrão HL7 (Health
Leavel Seven) a nível internacional.
O HL7 tem o objetivo de definir normas para a transmissão de dados como, por
exemplo, dados sobre registros de pacientes, admissão, transferências de pacientes, seguros,
taxas e contas a pagar, pedidos e testes de laboratório, exames de imagem, observações
médicas e de enfermagem, prescrições de dieta, pedidos à farmácia, pedidos de suprimentos e
arquivos; enfim, o padrão HL7 tem a capacidade de comunicar sistemas considerados
8
heterogêneos como o sistema administrativo de um hospital, o sistema financeiro e o sistema
de informações clínicas do paciente. (FERNANDES et al., 1999)
O HL7 não restringe o sistema operacional, nem a linguagem de programação a ser
utilizada para a troca de informação. Além disso, o HL7 é independente de meio físico e de
protocolo de comunicação. O padrão HL7 permite que o implementador defina suas próprias
mensagens, o que mais uma vez, torna-o flexível.(HENRIQUES, CARVALHO, 2005).
É a possibilidade de interoperabilidade entre os diversos sistemas da área de saúde
utilizando duas modelagens distintas, porém desenvolvidas conforme as especificações do
padrão HL7 que serão abordadas como objeto de estudo.
1.2 JUSTIFICATIVA
Em virtude da disparidade existente entre os sistemas projetados para o mundo dos
negócios e aqueles que englobam a área da saúde, tornou-se necessário à elaboração de
mecanismos que facilitem as transações num meio constituído por diversas peculiaridades
como é o serviço de saúde em qualquer lugar do globo.
Torna-se justificável realizar um estudo sobre uma tecnologia que tenha como
propósito solucionar alguns problemas encontrados em um ambiente de saúde:
• Evitar o desperdício de tempo cadastrando novamente um paciente em outra
aplicação pertencente ao mesmo sistema de saúde;
• Evitar repetição desnecessária de exames, laudos e imagens médicas quando o
paciente não puder apresentar o resultado dos seus exames (extravio de exames
de qualquer forma);
• Manter o histórico dos medicamentos em uso e já utilizados pelo paciente;
• Reduzir os custos operacionais;
• Manter a uniformidade e coerência dos dados.
Portanto, com a finalidade de utilizar um padrão de transmissão de informações em
Sistemas de Informação Hospitalares - os denominados HIS(Hospital Information System), e
constatar a importância de sua utilização, será implementado o padrão HL7 em um ambiente
fictício.
9
1.3 OBJETIVO
1.3.1 Objetivo Geral
Desenvolver modelos de sistemas que proporcionem a comunicação entre sistemas de
saúde heterogêneos através do uso do padrão HL7.
1.3.2 Objetivos Específicos
Para a implementação destes modelos de sistemas foram definidos como objetivos
específicos:
• Desenvolver um modelo que apresente uma arquitetura cliente-servidor e que
implemente o padrão HL7 diretamente na aplicação, denominado aqui de Modelo
Especializado;
• Desenvolver um modelo que contemple a arquitetura cliente-servidor de modo que
as aplicações existentes necessitem de uma mínima alteração para possibilitar a
troca de informações. Neste modelo, denominado Modelo Genérico, o padrão HL7
é implementado com a funcionalidade de um middleware HL7;
• Construir a gramática para estabelecer a estrutura de uma mensagem, através do
desenvolvimento dos analisadores léxico, sintático e semântico, a partir da
linguagem especificada pelo padrão HL7;
• Desenvolver aplicações para comprovar a transmissão de mensagens, cumprindo
com todos os requisitos e especificações da interface de comunicação exigidas pelo
middleware HL7.
1.4 MÉTODO
Para o desenvolvimento de um modelo de interoperabilidade de sistemas hospitalares
foi indispensável o conhecimento aprofundado do funcionamento da troca de mensagens
proposto pelo padrão HL7, bem como qual a versão do padrão HL7 deveria ser
implementada.
10
Depois de compreendida a especificação do padrão HL7, tornou-se necessário adquirir
o conhecimento de quais são as aplicações que constituem este ambiente de saúde, quais as
informações que uma aplicação deseja obter de outras aplicações (buscas), em qual sistema
operacional executa, em qual linguagem de programação foi desenvolvido. Para que assim, o
padrão HL7 seja implementado de acordo com as características de cada aplicação ou
sistema.1
Conhecidos os requisitos e características de cada sistema, partiu-se para o
levantamento de quais tipos de mensagens e segmentos deveriam ser implementados de
acordo com a versão do padrão HL7 selecionado.
Para o desenvolvimento dos modelos, partiu-se para a seleção da linguagem de
programação para implementar o padrão HL7, uma vez identificados os tipos de mensagens.
Constatou-se que esta linguagem deve suportar programação paralela e distribuída, para que
contemple a arquitetura cliente- servidor e multithreading.
A fim de satisfazer todas as exigências necessárias para a interoperabilidade utilizando
o padrão HL7, iniciou-se o desenvolvimento de um cliente e de um servidor, utilizando a
linguagem C++ e banco de dados PostgreSQL. Esta estrutura foi projetada, de forma que
possa ser operada tanto no ambiente Microsoft Windows quanto no Unix, e para que suporte
que um mesmo equipamento possa executar mais de uma aplicação utilizando apenas um
cliente HL7.
Uma vez implementado a arquitetura cliente servidor, implementou-se a gramática da
versão 2.3 do padrão HL7, porque está ainda é a versão mais utilizada no momento, utilizando
o aplicativo G.A.L.S. para gerar os analisadores léxico e sintático.
Ao nível de desenvolvimento e teste foram utilizados os sistemas operacionais
Microsoft Windows 2000, Microsoft Windows XP e Linux Slackware 10.1.
Para comprovar a utilidade do padrão HL7 foram desenvolvidas três aplicações que
simulam o funcionamento de um sistema hospitalar. As aplicações Cadastro de Pacientes e
Prontuário Eletrônico de Pacientes foram desenvolvidas em Object Pascal (Borland Delphi
7.0), já a aplicação Gerência de Exames foi desenvolvida em C++. Foram selecionadas
linguagens diferentes para comprovar que o uso dos modelos desenvolvidos é independente
de linguagens de programação e de plataforma.
1 Com o objetivo de cumprir o cronograma, os sistemas hospitalares também foram implementados para simular o comportamento de sistemas legados.
11
2 A INFORMATIZAÇÃO NA ÁREA DE SAÚDE A adoção da informática na área da saúde pode ser considerada como algo
relativamente novo. Seu desenvolvimento teve início na década de sessenta, onde as primeiras
atuações compreenderam análises estatísticas, epidemiológicas, sistemas de informatização
hospitalar e prontuários eletrônicos. (RODRIGUES, 2000).
Na década seguinte, o uso de recursos informatizados passou a ser utilizado também em
sistemas administrativos hospitalares. Com o aparecimento e desenvolvimento de
microcomputadores, tornou-se viável a informatização em sistemas de controle de farmácias e
laboratórios hospitalares. (RODRIGUES, 2000).
A década de noventa foi marcada por um aumento na utilização da informática em
gerenciamento e administração na área da saúde com o objetivo de suprir as crescentes
necessidades de organização exigidas pelo setor de saúde que se encontra em expansão
constantemente. (RODRIGUES, 2000).
2.1 VANTAGENS DA INFORMATIZAÇÃO NA ÁREA DA SAÚDE
A informatização na área da saúde tem como propósito atender às necessidades
operacionais dos diversos setores ou serviços, proporcionando uma integração e uma
interação entre as mais variadas unidades que compreendem um ambiente de saúde. Para isto,
foram projetados sistemas de informação para a saúde. Assim, geralmente, dentro de um
sistema que rege um hospital, podem-se encontrar subsistemas com módulos do laboratório
central, da admissão, da radiologia, do centro cirúrgico entre outros; estes diversos módulos
apresentam vantagens como total acesso às informações produzidas em cada setor do hospital,
maior facilidade e agilidade em pedidos de exames, localização rápida e fácil do histórico
clínico do paciente, possibilitando desse modo, a recuperação de diagnósticos decorridos
anteriormente, internações, consultas ambulatoriais, exames antigos ou ainda cirurgias
realizadas. (SILVA et al., 1999).
Através de um sistema de informação em saúde, ainda é possível obter-se um controle
total sobre os leitos hospitalares, evitando a subutilização e possibilitando uma noção mais
precisa das necessidades de cada enfermaria em um dado momento (remédios, refeições,
aparelhos e instrumentos, entre outros). O módulo na emergência irá cadastrar todos os
atendimentos ocorridos nos setores de emergência ou pronto atendimento. Assim, evita-se o
12
alto índice de perda de documentos que ocorre nas emergências e maior agilidade durante
atendimento. O módulo do laboratório ainda pode permitir armazenamento de resultado de
exames, coleta, requisições, permitindo assim a possibilidade do laboratório estar localizado
dentro do hospital ou ser independente do hospital. (SILVA et al., 1999).
Estas podem ser consideradas como facilidades iniciais proporcionadas pela
informatização na área da saúde, que ainda traz como conseqüência o exercício da
telemedicina junto com o avanço da telecomunicação, permitindo assim transferência de
dados eletrônicos (como imagens de alta resolução, som, imagens ao vivo e informações do
paciente) de um local a outro através de um sistema de redes e telecomunicações. Assim, a
telemedicina possibilita que o diagnóstico médico possa ser realizado mesmo estando o
paciente geograficamente distante do centro médico. (RODRIGUES, 2000).
2.2 INFORMATIZAÇÃO DA SAÚDE NO MUNDO
Nos últimos anos, houve uma maior preocupação com a informatização na área de
saúde em todo o mundo. Os investimentos em sistemas de saúde nos países desenvolvidos, é
considerável. Podemos citar como um dos principais interessados, os Estados Unidos, que
buscam por novas tecnologias e investem constantemente na interoperabilidade de sistemas.
(VASCONCELLOS, 2004).
Nos Estados Unidos, a discussão sobre padrões de informação em saúde está em
evidência. Existe uma busca para a constituição de uma infra-estrutura de informação em
saúde, que permita a troca de informações e a avaliação de riscos à saúde. Com esse objetivo,
o governo norte-americano adotou recentemente cinco padrões para uso na integração entre
agências federais: HL7, NCDCP (National Council for Prescription Drug Programs), IEEE
1073 (Standard for Medical Device Communications), DICOM (Digital Imaging and
Communications in Medicine) e LOINC (Logical Observation Identifiers Names and Codes).
Esses padrões são elaborados com o objetivo de promover a interoperabilidade de sistemas,
mas ainda não está se verificando a interoperabilidade de componentes de diferentes
fornecedores. (VASCONCELLOS, 2004).
Nos últimos dez anos uma nova tendência surgiu nos Estados Unidos - a integração
entre serviços e instituições de saúde. Este movimento iniciou com a consolidação do setor de
saúde privado americano ao redor de conglomerados (Integrated Healthcare Delivery
13
Systems). Os conglomerados se formam pela junção de instituições que tinham histórias,
sistemas e tradições diferentes, cujos sistemas, a partir do momento da fusão, devem ser
integrados. Assim, necessitou-se o desenvolvimento de uma nova tecnologia que é o cadastro
central de pacientes, ou MPI (Master Patient Index ou Cadastro Integrado de Pacientes). O
MPI permite ligar múltiplos domínios cadastrais com informações sobre pessoas e pacientes.
Assim, é possível reunir todos os dados de saúde de um indivíduo ao longo do processo de
assistência à saúde. Este tipo de integração permite melhor faturamento, melhores análises do
uso dos serviços e dos resultados. (VASCONCELLOS, 2004).
Já quando se trata da informatização em países em desenvolvimento, é possível
identificar um grave atraso da informatização na área da saúde, principalmente ao nível de
armazenamento e compartilhamento de informações médicas. Os motivos de tal atraso podem
ser citados como:
• Forma de trabalho do profissional de saúde: profissionais da área da saúde pouco
habituados com o uso de computadores nas tarefas diárias e contrárias às regras que se
fazem necessárias para que a implantação de sistemas de informática seja bem
implementada;
• Estrutura organizacional dos sistemas de saúde: desatualização e despreparo para as
mudanças sociais que o avanço tecnológico vem trazendo à sociedade;
• Custos elevados: como o investimento inicial de um processo de informatização é
relativamente alto, muitas vezes opta-se pela manutenção do sistema tradicional.
(SAÚDE TOTAL, 2000).
2.3 INFORMATIZAÇÃO DA SAÚDE NO BRASIL
Para se manter adaptado às novas tecnologias, o Brasil, também passou a investir na
informatização hospitalar e ambulatorial. Mediante esse interesse, verificou-se que existem
algumas particularidades nos sistemas de saúde brasileiros devido ao contexto econômico,
político, social, cultural, histórico, entre outros. Uma das características mais notáveis nos
sistemas de saúde do Brasil refere-se ao enfoque, que é predominantemente administrativo,
priorizando procedimentos de contabilidade, como a prestação de contas e o faturamento, em
detrimento de todos os demais aspectos, ou seja, a informação clínica é deixada para segundo
plano. Um outro ponto de destaque relevante é que os sistemas hospitalares e ambulatoriais
14
brasileiros divergem consideravelmente nos setores públicos e privados, isto ocorre em
conseqüência da prioridade atribuída ao faturamento que possui regras diferentes em cada
setor. (VASCONCELLOS, 2004).
No Brasil é muito comum a fragmentação dos sistemas de informação em saúde. Segundo Vasconcellos(2004):
“Organizações complexas de saúde quase sempre utilizam vários sistemas de informação em paralelo: um ou mais sistemas centrais para dar conta do atendimento principal; sistemas administrativos; sistemas departamentais; entre outros. Cada um destes sistemas possui uma parte da informação sobre os pacientes e sobre as atividades da instituição, gerando necessidade continuada de integração entre sistemas heterogêneos. A solução mais comum para este problema é o uso de interfaces ad hoc entre os vários sistemas. A implementação e uso destas interfaces podem ser vistas como uma solução trabalhosa, mas mesmo assim, deve continuar sendo muito popular no curto e médio prazo, principalmente considerando que muitos sistemas legados continuarão sendo utilizados por tempo indeterminado. Portanto, a adoção de padrões de informação em saúde facilita e torna mais direto o desenvolvimento destas interfaces, reduzindo muito o seu custo. “
Apesar da utilização da Internet ser consideravelmente difundida, a integração dos
sistemas de informação em saúde brasileiros com a Web ainda não atinge a mesma proporção.
Já existem iniciativas para permitir acesso através da internet a algumas funcionalidades, tais
como resultados de exames, agendas, cadastros, entre outras. (VASCONCELLOS,2004).
Os sistemas brasileiros aos poucos começaram a oferecer recursos para facilitar a
emissão de laudos. Atualmente, estão finalmente se voltando para o tratamento das imagens e
alguns o estão fazendo seguindo padrões internacionais como PACS (Picture Archive and
Communications System) e DICOM.
A essência dos sistemas de informação na área de saúde está no prontuário eletrônico
de paciente, que deve reunir informações de todo o processo de atendimento. Apesar de
existirem dificuldades técnicas, o prontuário eletrônico está ganhando aceitação de maneira
acelerada, isto se deve também porque o preconceito e a dificuldade das novas gerações de
médicos em lidar com a informática está desaparecendo. Estudos realizados nos Estados
Unidos indicam que quando o sistema de prescrição é parte de um conjunto de sistemas
clínicos seu uso é aceito mais rapidamente pelos médicos; e também consideram que a adoção
de sistemas de prescrição eletrônica é uma estratégia para a diminuição de erros médicos e a
melhoria da qualidade da assistência. (VASCONCELLOS,2004).
15
De acordo com Vasconcellos(2004), legalmente, no Brasil, o prontuário eletrônico
ainda não pode substituir o papel. Além disso, há muitas questões tecnológicas envolvidas no
projeto do prontuário eletrônico: a representação dos conceitos clínicos por meio de
vocabulários adequados, a modelagem conceitual dos dados clínicos, a tecnologia de
armazenamento a ser utilizada, entre outras.
Uma vez que a modelagem conceitual dos dados clínicos é pouco estruturada a
tecnologia de bancos de dados relacional não é eficiente. A tecnologia XML (Extensible
Markup Language) está gradativamente assumindo um papel relevante e cumprindo a
promessa de se tornar a principal ferramenta de suporte ao prontuário eletrônico e ao
intercâmbio de informações entre sistemas. A versão 3.x do padrão HL7 é baseada em XML,
podendo então facilitar a integração.
Mas mesmo com o uso das tecnologias citadas acima, ainda persiste dois problemas
em se tratando da identificação de pacientes: o problema de estabelecer corretamente a
identidade da pessoa que se apresenta a um serviço de saúde, ou seja, um problema de
identificação; e o problema de estabelecer referências cruzadas entre as informações relativas
a uma mesma pessoa, porém armazenadas em diferentes sistemas ou bancos de dados; ou
armazenadas em um mesmo sistema, no entanto vinculadas a eventos ou momentos
diferentes, ou seja, um problema de integração.(VASCONCELLOS, 2004).
O problema de identificação é muito importante sempre, mas é crucial para os
prestadores de serviços privados de saúde e planos de saúde, que precisam estabelecer com
precisão e antecipadamente se um determinado paciente, é ou não, elegível para receber
algum tratamento. A solução mais simples e comum para o problema de identificação, embora
de eficácia limitada, é estabelecer documentos de identificação, através de cartões. Novas
técnicas e abordagens incluem o uso de biometria e reconhecimento de digitais. A HIPAA
(Health Insurance Portability and Accountability Act) norte-americana prevê o uso de
biometria como alternativa para identificação de pessoas e esse tipo de recurso já começa a
ser usado também no Brasil. (VASCONCELLOS, 2004).
No Brasil, o Cartão Nacional de Saúde é a mais abrangente iniciativa de identificação
de pacientes. Até meados de 2004, cerca de dois terços da população brasileira já estavam
recadastrados. A integração do sistema Cartão Nacional de Saúde com os demais sistemas
públicos de saúde ainda não foi concluída, mas o processo está avançando.
(VASCONCELLOS, 2004).
16
No que se refere ao uso de software no Brasil para o setor de saúde, pode-se destacar
que a utilização de software livre é considerada pequena, mas crescente. Apesar do
pioneirismo de outros países no desenvolvimento de sistemas de saúde, no Brasil a aquisição
de sistemas provenientes do mercado externo não é significativa devido às particularidades
das normas governamentais brasileiras não estarem implementadas nestes sistemas.
(VASCONCELLOS, 2004).
Quanto ao uso de hardware, podemos considerar que o computador de mão pode
contribuir muito para a informatização na área de saúde se convertendo então, a médio prazo,
na tão esperada clinical workstation, ou seja, na interface mais comum entre os médicos e os
sistemas computadorizados de informação em saúde. (VASCONCELLOS, 2004).
Foi estabelecido em 2002, que a SBIS (Sociedade Brasileira de Informática em Saúde)
seria responsável pela certificação de software na área da saúde, para atender às normas
técnicas para o Uso de Sistemas Informatizados para a Guarda e Manuseio do Prontuário
Médico, da resolução 1639/2002 do Conselho Federal de Medicina. A SBIS também é
responsável pela elaboração do Manual de Requisitos de Segurança, Conteúdo e
Funcionalidades para Sistemas de Registro Eletrônico em Saúde (RES), que consolida os
requisitos para que Sistemas RES possam ser certificados pela SBIS e o Conselho Federal de
Medicina. Outra importante iniciativa da SBIS é a adoção de um Código de Ética para os
Profissionais de Informática em Saúde. O Código limita-se a questões éticas vinculadas à
relação entre o especialista em Informática em Saúde e seus interlocutores (pacientes,
profissionais da saúde, pessoal administrativo, instituições de saúde, operadoras de planos de
saúde e agências governamentais, etc.) e não inclui qualquer referência aos procedimentos
técnicos. (FRICK, LORO, 2004).
Considerando o retorno proporcionado pela informatização com o surgimento de
tecnologias concebidas especialmente para a área da saúde, cabe às instituições brasileiras,
investirem seu capital a fim de manterem-se sintonizadas com as novidades e partir para a
adoção de tecnologias que respeitem a usabilidade necessária no ambiente de saúde.
17
3 INTEROPERABILIDADE DE SISTEMAS
“Interoperabilidade é a habilidade de dois ou mais sistemas (computadores, meios de comunicação, redes, software e outros componentes de tecnologia da informação) de interagir e de intercambiar dados de acordo com um método definido, de forma a obter os resultados esperados.” (BRASIL, 2005).
Interoperabilidade não é somente a integração entre sistemas e nem somente a
integração de redes. Não referencia unicamente troca de dados entre sistemas e não contempla
simplesmente definição de tecnologia. Na verdade, é uma soma de todos esses fatores,
considerando, também, a existência de um legado de sistemas, de plataformas de hardware e
software instaladas. A interoperabilidade tem por meta a consideração de todos os fatores para
que os sistemas possam atuar cooperativamente, fixando as normas, as políticas e os padrões
necessários para consecução desses objetivos. (BRASIL, 2005).
3.1 INTEROPERABILIDADE DE SISTEMAS HOSPITALARES
Com a evolução da tecnologia e a crescente informatização em ambientes clínicos e
hospitalares no início dos anos noventa, começou-se a pensar na distribuição das informações
armazenadas nestes sistemas. Desta forma, a interoperabilidade entre os sistemas hospitalares
passou a ser o principal foco de estudo para a disseminação das informações médicas.
Porém, quando se pensa em interoperabilidade, na qual sistemas "conversam" com
outros sistemas e com equipamentos biomédicos, não se pode deixar de pensar em padrões.
Para essa interoperabilidade entre os sistemas é necessário que os dados sejam estruturados e
não ambíguos. Além disso, é fundamental ter dados com uma semântica que possa ser
compreendida pelos outros sistemas (COSTA, 2001).
3.1.2 Padronização A padronização da informação em saúde é necessária devido a diversos fatores, dos
quais pode-se destacar segundo Costa (2001):
• Diversidade de fontes e termos (existem mais de 150.000 conceitos médicos);
18
• Sistemas estão em diferentes plataformas de software e hardware,
necessitando de uma linguagem comum (padrão) para que esses possam
intercambiar informações;
• Para facilitar a busca e a comunicação de informações;
• Devido a pontos importantes para a área da saúde como estatística,
epidemiologia, prestação de contas (faturamento), indexação de documentos e
pesquisa clínica;
• Viabilizar o uso de sistemas de apoio à decisão e sistemas de alerta, que são
indispensáveis para a interoperabilidade entre os sistemas.
Os padrões elaborados para a área de saúde podem ser classificados segundo Costa
(2001) em:
• Identificação: para pacientes (Social Security Number - nos Estados Unidos,
Cartão Nacional de Saúde no Brasil), médicos (Número no Conselho Regional
de Medicina);
• Comunicação: padrão para mensagens entre sistemas (HL7, X12, EDIFACT
(Electronic Data Interchange For Administration, Commerce and Transport),
XML;
• Conteúdo e Estrutura: padronização do Registro Clínico do DATASUS
(Departamento de Informação e Informática do Sistema Único de Saúde),
ABRAMGE (Associação Brasileira de Medicina de Grupo);
• Representação de dados clínicos (Códigos): CID (Código Internacional de
Doenças), SNOMED (Systematized Nomenclature of Medicine), LOINC, AMB
(Associação Médica Brasileira);
• Confidencialidade, Segurança e Autenticação;
• Indicadores de Qualidade, Conjunto de Dados e Diretrizes.
Além desses existem padrões para imagens, como o DICOM e padrões para objetos,
como o CORBAMed.
No Brasil, quanto à padronização, é possível identificar o DATASUS como
responsável pela padronização das informações médicas do país. De acordo com Costa
(2001), um exemplo de padronização brasileira é o comitê de Padronização de Registros
19
Clínicos (PRC) que aprovou, através de um processo aberto, um conjunto mínimo de dados
que um PEP deve ter, além de elaborar a Document Type Definition (DTD) correspondente à
estrutura de dados proposta pelo PRC para troca de dados via XML.
Ainda no Brasil, outro exemplo de padronização é o chamado Padrão ABRAMGE,
utilizado para a troca de faturamento entre prestadores de serviços médicos e as operadoras de
planos de saúde. (COSTA, 2001).
Porém existe a necessidade de adoção de um padrão a nível internacional que
regularize a troca de informações dentro de um contexto global. Com esta finalidade que
surgiu o padrão de comunicação para troca de dados clínicos como o HL7.
20
4 TECNOLOGIAS E PADRÕES A fim de produzir modelos de interoperabilidade de sistemas hospitalares, é necessário
entender o que é o padrão HL7, seu princípio, suas versões publicadas, suas potencialidades
para que então possa ser realizada a construção de sua gramática.
Já para a implementação de uma arquitetura cliente servidor é necessário conhecer
quais recursos e funcionalidades que a linguagem selecionada proporciona para programação
paralela e distribuída para atingir os objetivos esperados.
São as tecnologias selecionadas para concepção deste modelo de interoperabilidade
que serão abordadas a seguir.
4.1 HL7
Em 1987, foi fundada uma organização sem fins lucrativos denominada Health Level
Seven, com a responsabilidade de produzir normas para a área de saúde, relacionados com a
informação clínica e administrativa. A sede da instituição está localizada nos Estados Unidos,
mas conta com delegações de vinte e sete países como Alemanha, Argentina, Austrália,
Brasil, Canadá, China, Coréia, Croácia, Dinamarca, Espanha, Finlândia, França, Grécia,
Holanda, Índia, Irlanda, Itália, Japão, Lituânia, México, Nova Zelândia, Polônia, Reino
Unido, República Tcheca, Suíça, Taiwan e Turquia. Teve seu reconhecimento
internacionalmente, pela ANSI (American National Standards Institute) em 1994.
O estabelecimento das normas é realizado pelos seus membros, que incluem
organizações governamentais, vendedores, consultores e engenheiros de sistemas. A
organização atualmente é constituída por mais de 2000 membros, representando
aproximadamente 500 organizações, sendo que 90% dos principais fabricantes/fornecedores
de sistemas de informação clínica dos Estados Unidos estão incluídos. Estes se encontram
divididos em Grupos de Trabalho, que por sua vez são organizados em comitês técnicos e
grupos de interesses especiais. Os comitês técnicos são diretamente responsáveis pela
especificação das normas, enquanto os grupos de interesses especiais investigam novas áreas
que possam ser incluídas nas especificações do HL7.
O HL7 tem por missão:
21
“Promover o desenvolvimento de normas relacionadas com a troca, integração, partilha e recuperação de informação eletrônica na saúde, assim como no apoio à prática médica e administrativa e avaliação dos serviços de saúde. Em concreto, a sua missão orienta-se para o desenvolvimento de uma linguagem flexível, de baixo custo, possível de parametrizar, seguindo uma metodologia que permita a interoperabilidade entre os mais diversos sistemas de informação na área de saúde”.
A origem do termo Level Seven está associada à camada de mais alto nível do modelo
de comunicação da ISO/OSI, ou seja, a camada de aplicação. A camada de aplicação
relaciona-se com a implementação de sistemas abertos, portanto, não é necessária nenhuma
restrição quanto à comunicação, tipo de rede ou meio físico, resultando apenas como função a
definição da estrutura da informação para trocar, a seqüência e instantes para o fazer e as
correspondentes mensagens de confirmação ou erro. Dessa forma, estabeleceu-se que o
padrão HL7 tem a finalidade de definir o conteúdo e formato das mensagens que poderão ser
trocadas na camada de aplicação. (HENRIQUES, CARVALHO, 2005).
A figura 1 mostra as sete camadas do modelo ISO/OSI.
Figura 1 – Modelo ISO / OSI
4.1.1 Princípios Básicos do Padrão HL7
4.1.1.1 Visão Geral do HL7
A unidade básica de informação, a ser trocada entre os vários intervenientes, é
designada no padrão HL7 por mensagem. O padrão especifica além das características
principais da troca de mensagens entre sistemas distintos, os vários tipos de mensagem e
respectiva constituição.
Transporte
Sessão
Enlace
Rede
Apresentação
Aplicação
Física
Protocolo de Aplicação
Protocolo de Apresentação
Protocolo de Sessão
Protocolo de Transporte
Protocolo de Rede
Protocolo de Enlace
Protocolo de Física
Transporte
Sessão
Enlace
Rede
Apresentação
Aplicação
Física
22
Genericamente uma mensagem é constituída por segmentos, que por sua vez são
constituídos por campos, sendo estes últimos organizados em componentes.
Tipos de Mensagens e Formato
O padrão especifica quais as mensagens a serem trocadas entre os diversos setores do
hospital. Dentre os vários tipos de mensagens definidos pelo HL7 podem ser citadas: gestão
de pacientes ao nível de admissão, transferências e saídas, pedidos, resultados, observações
clínicas e contabilidade.
O padrão HL7 define o formato das mensagens, ou seja, descreve como os dados
devem ser representados, o seu tipo e quais os caracteres usados para delimitar os vários
segmentos numa mensagem.
Eventos
O padrão HL7 especifica as circunstâncias em que as mensagens devem ocorrer,
regras como conseqüência de eventos (trigger event). Por exemplo, se for definido que os
dados pessoais de um paciente devem ser disponibilizados, então quando houver uma
admissão de paciente, automaticamente deve ser desencadeada uma mensagem que transmita
a informação a todos os interessados (consulta não solicitada – unsolicited message).
Confirmação/Erro
O padrão HL7 também define os procedimentos que devem ser realizados na
ocorrência de certos erros entre as aplicações. Por exemplo, caso uma aplicação não receba os
dados que esperava, esta pode comunicar o fato para a aplicação emissora através de uma
mensagem de erro. Por outro lado, se a transmissão dos dados foi efetuada corretamente,
então uma mensagem de confirmação (ACK-acknowledge) pode igualmente ser enviada.
Garantindo, portanto confiabilidade na troca de mensagens.
4.1.1.2 Arquitetura Física do Sistema
Quanto a arquitetura física do sistema definida pelo padrão HL7 tem-se:
• O sistema computacional pode estar organizado de uma forma central ou
distribuído;
23
• A totalidade das especificações definidas na norma HL7 não necessita
obrigatoriamente ser implementada.
• A troca de informação pode ser efetuada com base em diversos sistemas
operacionais ou linguagens de programação.
• O padrão HL7 é independente do meio físico e protocolo de comunicação,
podendo, por exemplo, ser usado o protocolo TCP/IP.
4.1.1.3 Interoperabilidade
Como um dos objetivos principais do padrão é ser o mais abrangente possível, o HL7
não assume qualquer arquitetura particular para o sistema de comunicação, pelo contrário,
pretende servir de suporte à comunicação entre diversas aplicações distintas permitindo seu
uso inclusive em sistemas heterogêneos.
Devido, a complexidade de informações existentes num sistema hospitalar e a
inexistência de um vocabulário comum, levou-se a adoção de uma abordagem onde é
atribuída liberdade ao utilizador de implementar as suas próprias mensagens. Porém, esta
abertura, apresenta resultados indesejáveis, por exemplo, muitas vezes é necessário que uma
grande quantidade de analistas para priorizar a construção e implementação do padrão, ou
seja, campos e/ou segmentos opcionais, ambíguos e talvez até mesmo inconsistentes,
requerem o estabelecimento de uma interpretação única e objetiva por parte dos projetistas
dos sistemas participantes. Em outras palavras, devido à flexibilidade do padrão HL7, embora
a estrutura sintática das mensagens esteja claramente definida, a semântica é demasiadamente
livre para acomodar qualquer possibilidade essencial na formação do modelo.
Já que é responsabilidade da aplicação proceder à adaptação das suas estruturas de
dados às normas definidas pelas mensagens HL7, a implementação desta última não é, um
procedimento trivial. Isto porque é necessário considerar um esforço inicial de
desenvolvimento e a sua implementação é uma tarefa trabalhosa. Sendo assim, o problema foi
reconhecido, tanto pela instituição HL7 como por aqueles que desenvolvem ferramentas
computacionais. Com a finalidade de facilitar esta tarefa inicial, têm surgido diversas
aplicações comerciais capazes de automatizar o processo de implementação prática do padrão.
A correta implementação do padrão HL7 permite:
24
• Interoperabilidade - Fornece formatos e protocolos para a troca de informação
entre diversos sistemas computacionais na área da saúde, permitindo desta forma
integrar equipamentos distintos;
• Flexibilidade - Pode ser implementada computacionalmente utilizando uma grande
variedade de softwares;
• Tornar possível a unificação de interfaces distintas, uma vez normalizados os
vários formatos existentes;
• Minimizar o número e o tempo necessários à implementação de interfaces;
• Melhorar os meios de suporte à decisão pela capacidade de integrar informação
clínica entre os vários setores do hospital.
4.1.2 Versões Publicadas
4.1.2.1 HL7 Versão 1
A primeira versão, 1.0, foi apresentada em 8 de outubro de 1987 e definia o âmbito e o
formato das mensagens. Apesar de várias funcionalidades terem sido sugeridas muitas delas
não foram, contudo implementadas, por exemplo, as relativas aos dados contabilísticos do
paciente.
4.1.2.2 HL7 Versões 2.x
A versão 2.0 surgiu em setembro de 1988 e depois desta uma série de atualizações
foram realizadas. A versão 2.1, surgida em junho de 1990, constituiu a primeira versão a ser
reconhecida e efetivamente adotada (nos Estados Unidos). Em junho de 1994 a HL7 torna-se
membro certificado pela ANSI e em dezembro desse mesmo ano surge à versão 2.2, e a sua
aplicação começa na prática a ser amplamente implementada. Em março de 1997, são
proporcionadas funcionalidades (já bem estabelecidas) para a troca de informação relativa à
gestão do paciente (admissão, transferências e saídas), contabilidade, observações clínicas,
gestão da informação médica, entre outras. Apesar de em 1998 ter sido introduzida a versão
2.3.1 e em 2000 a versão 2.4. A versão 2.3 continua a ser a mais utilizada na prática, sendo
atualmente apenas válidas as versões 2.3 ou superiores. (HENRIQUES, CARVALHO, 2005).
De fato, referem-se como aspectos negativos das versões 2.x os seguintes:
• Processo de integração é complexo e moroso;
• Algumas especificações permitem interpretações distintas;
25
• Demasiadas opções;
• Falta de suporte a novas tecnologias, tais como:
o Web
o Orientadas a objetos
o XML
o Segurança.
4.1.2.3 HL7 Versão 3
Desde 1996 a organização HL7 está trabalhando no desenvolvimento da versão 3, fato
motivado principalmente pelas limitações das versões 2.x. Assim, a versão 3 é radicalmente
distinta da versão 2 em vários aspectos. A versão 3 recebeu certificação da ANSI no dia 05
maio de 2005. (HL7, 2005).
Em primeiro lugar, o padrão terá por base um modelo orientado a objetos RIM
(Reference Information Model) que proporciona uma visão coerente dos dados a serem
trocados assim como das relações entre os diferentes tipos de dados. Assegurando assim, que
as mensagens sejam consistentes e que na prática sejam verdadeiramente utilizáveis pelas
aplicações comunicantes. Esta abordagem produz um número maior de eventos e de formatos
de mensagens, mas permite a obtenção de mensagens mais precisas, isto é, com muito poucas
opcionalidades.
Em segundo lugar, as mensagens serão desenvolvidas seguindo a metodologia MDF
(Message Development Framework). Este processo envolve o desenvolvimento de diversos
modelos, incluindo o RIM já referido e o PRA (Patient Record Architecture). O objetivo
principal dessa abordagem será produzir objetos consistentes e a sua representação através de
mensagens. A versão 3 também permite a extensão para diferentes formatos de troca de
informação. A versão 2.x permite apenas um formato baseado em caracteres ASCII. A versão
3 suporta XML, ActiveX e Corba. Para proporcionar a interoperabilidade, a versão 3 tira
proveito das funcionalidades do XML, desenvolvendo uma arquitetura baseada em XML, a
CDA (Clinical Document Architecture), que proporciona um modelo de trocas com diversos
níveis de complexidade e que permite a criação de documentos XML que incorporam
mensagens HL7.
26
4.1.3 Potencialidades do HL7
A adoção do padrão HL7 é considerada importante para o ambiente hospitalar pelos seguintes motivos:
• Aceitação: a norma encontra-se em uso por um número significativo de países,
Estados Unidos, Japão, Canadá, Inglaterra, França, são alguns exemplos. No caso
dos Estados Unidos registra-se o uso do padrão HL7 em 90% das instituições
hospitalares, o que por si só justifica a sua utilização;
• Versatilidade: permite satisfazer necessidades tanto no nível de instituições (entre
hospitais) quanto no nível dos setores de um hospital (farmácia, laboratórios,
enfermarias);
• Sistema aberto: tem por base padrões não proprietários, que resultam da
cooperação entre diversas entidades, por exemplo, utilizadores, fornecedores,
engenheiros de sistemas;
• Reconhecimento: por ser formada a partir da cooperação entre diversas
instituições hospitalares a instituição HL7 é certificada pela ANSI e pela ISO;
• Ativa: a instituição incentiva e promove a cooperação entre os principais
interessados na área da saúde para assegurar que as normas produzidas atendam às
necessidades dos interessados;
• Flexibilidade: permite um grau de liberdade para suprir as necessidades especificas
de um utilizador, além de possuir uma grande variedade de mensagens pré-
definidas.
4.1.4 Utilizações das versões 2.x
A utilização da versão 2.3 (e mais recentes) nos sistemas de informação na área de
saúde deve-se a grande quantidade de equipamentos em atividade que usam efetivamente esta
norma e consequentemente condicionam a evolução da norma. Por outro lado, como ainda
existem incertezas a respeito da versão 3, torna-se claro que a maioria dos fabricantes
continuará a suportar a norma HL7 2.x, o que virá ainda mais a reforçar o seu potencial.
(HENRIQUES, CARVALHO, 2005).
Dessa forma, mesmo que os equipamentos venham a suportar versão 3, ainda assim,
terão que manter a compatibilidade com a versão 2.x, e a transição não será nunca um
27
processo instantâneo. Como recentemente a certificação da ANSI foi recebida, calcula-se que
a utilização efetiva da versão 3 será a partir de 2006.( HENRIQUES, CARVALHO, 2005).
A instituição HL7 afirmou que futuras revisões na norma 2.x serão mínimas ou mesmo
inexistentes. Uma das exceções, e que constitui uma área recentemente desenvolvida,
relaciona-se com mecanismos capazes de codificar a norma 2.x para XML, seguindo um
algoritmo formal, o que veio reforçar ainda mais o potencial da versão 2.x. (HENRIQUES,
CARVALHO, 2005).
4.1.5 Especificação da Versão 2.3
Uma vez que a comunicação entre duas aplicações é realizada através do envio de
mensagens, tudo se inicia quando ocorre algum tipo de evento. Como primeiro exemplo de
evento temos quando o emissor envia uma mensagem ao receptor a fim de repassar alguma
informação como a admissão de paciente, ou seja, a mensagem não solicitada. Um outro tipo
de evento pode ser desencadeado quando o emissor solicita alguma informação ao receptor.
Portanto, seja qual for à situação, uma mensagem surge sempre de uma de duas razões: de
uma consulta ou de um evento que desencadeia uma mensagem (não solicitada).
Em resposta as mensagens, podem ser solicitadas informações de confirmação de
recebimento. A figura 2 mostra o envio de uma mensagem através do evento admissão de
paciente(ADT ^ A01) que exige confirmação de recebimento. Neste caso, as aplicações
receptoras enviam uma mensagem de confirmação de recebimento para o emissor.
Figura 2 – O evento "admissão de paciente" origina o envio automático de informação
28
4.1.5.1 Composição da Mensagem
4.1.5.1.1 Mensagens No padrão HL7 uma mensagem é a menor unidade de dados que pode ser transferida
entre dois intervenientes. É constituída por segmentos e é delimitada por um inicio de bloco,
caractere Hex0b, e por um fim de bloco, o caractere Hex1c seguido de Hex0d (carriage return
<cr>). A figura 3 representa uma mensagem HL7.
Figura 3 – Formato de uma mensagem HL7
4.1.5.1.2 Segmentos Um segmento é cada uma das partes que constituem uma mensagem, disponibilizando um
meio de agrupar informação de uma forma lógica. Os segmentos são representados por um
conjunto de três caracteres (bytes), por essa razão são designados como identificadores do
segmento. A presença de um segmento numa determinada mensagem pode ser obrigatória (R-
required), opcional (0-optional) ou então pode ocorrer mais que uma vez, isto é, pode ser
repetida. Quando for opcional utilizam-se colchetes [ ] para indicar o fato, por exemplo, a
notação [AL1] significa que o segmento relacionado com alergias é opcional nessa
mensagem. De forma análoga, a notação { } permite especificar a repetição de um segmento,
assim {AL1} indica que o segmento das alergias pode ser repetido se for necessário. As
notações anteriores podem ser combinadas, [{AL1}] significando que o segmento das alergias
é opcional e que, além disso, pode ser repetido.
4.1.5.1.3 Campos O conteúdo semântico da mensagem, como os detalhes relativos do paciente, está
definido nos campos que constituem cada um dos segmentos. Os campos têm comprimento
variável e encontram-se separados entre si pelo caractere “|”.
Os campos por sua vez são constituídos por cadeias de caracteres sendo cada um deles
pertencente a um dado tipo, como por exemplo, o tipo ST representa uma string. Para cada um
dos tipos de dados poderá ainda ter associado um valor obtido a partir de uma tabela. Por
exemplo, o tipo de dados sexo (do tipo String), será definido pelos seguintes valores (F-
Feminino, M-Masculino, H–Hermafrodita, T-Transexual, U-Desconhecido).
<Hex 0b> <HL7 Message segments> <cr> <Hex 1c><cr>
29
Os campos ainda podem ter uma estrutura bem definida, de que é exemplo o tipo de
dados DT-date, definido por YYYYMMDD. Desta forma o dia 28 de Maio de 2005 define-se
por 20050528.
Caso o campo transmitido contenha uma cadeia de caracteres vazia (|””|), então isso
significa que o campo está vazio, ou seja, o receptor deverá eliminar os valores representados.
4.1.5.1.4 Componentes e sub-Componentes Alguns campos podem conter vários componentes, sendo neste caso delimitados pelo
separador “^”. Estes campos são usualmente designados por campos compostos, de que o
campo nome do paciente é um exemplo, uma vez que pode conter vários componentes, por
exemplo, nome próprio e apelido.
Concluindo, uma mensagem é constituída por segmentos, que por sua vez são
constituídos por campos, subdivididos em componentes. A figura 4 representa a constituição
de uma mensagem.
Figura 4 – Constituição de uma mensagem de identificação de paciente(A01)
4.1.5.2 Construção de Mensagens
O padrão HL7 distingue diversos tipos de mensagens. Por exemplo, na figura 5
mostra-se o exemplo de uma mensagem relativa à admissão de um paciente (ADT^A01),
composta pelos segmentos MSH, EVN, PID.
Figura 5 – Mensagem HL7 para admissão de paciente
MSH|^~\&|ADT1|CADPACIENTE|LABADT|CADPACIENTE|20052242048||ADT^A01|MSGADT1200522420485|P|2.3<cr> EVN|A01|20052242048<cr> PID|||PATID1234 5 M11||Silva^Marcelo^Loyola^II^Dr.|Maria Ferreira|19451227|M||A|Rua Minas Gerais 55||(48)9833-8956|(48)398-6665|Português|S|lutheran |989896632|96556|98989^055^65||U|São Paulo|0|3|Brasileiro||Brasileiro||<cr>
30
Podemos considerar os principais tipos básicos de mensagens:
• Gestão de pacientes ou ADT - relativas à gestão da estada do paciente no hospital,
admissão, transferência e saídas de pacientes por esta razão também conhecidas
por mensagens ADT-admissions, discharges, transfers. São desencadeadas sem
uma consulta prévia explícita (unsolicited messages);
• Pedidos/Querys – utilizadas para requisição (consulta) de informação. Uma
situação típica ocorre quando um dos setores (farmácia, laboratório) consulta o
sistema de admissão com respeito à informação de um paciente;
• Resultados/Observações – Resulta como resposta a uma mensagem do tipo
(pedido/query) e a sua função é permitir a transmissão de observações clínicas, por
exemplo;
• Outras - relativas à informação contabilística e financeira, experiências clínicas,
bases de dados, etc.
Todas as mensagens têm uma estrutura semelhante, por exemplo, o primeiro segmento
é sempre o cabeçalho (MSH) contendo informação sobre a mensagem e seu conteúdo.
Seguem-se depois os demais segmentos, específicos de cada uma das mensagens. A figura 6
exibe os principais tipos de mensagens do padrão HL7.
Figura 6 – Alguns tipos de mensagens
4.1.5.2.1 Mensagens Relativas a Gestão de Pacientes (ADT)
É utilizado para transmitir informação relativa à admissão, transferência e saída de um
paciente no hospital. Dependendo do tipo de evento (trigger event) é possível distinguir vários
31
tipos de mensagens. O evento que origina a mensagem é a admissão do paciente (A01), sendo
uma mensagem para a qual não houve um pedido explicito (unsolicited). Como regra geral
esta informação deve ser difundida para os vários setores (enfermarias, laboratórios, etc). A
figura 7 mostra os segmentos de uma mensagem de admissão de paciente.
Figura 7 – Admissão de Paciente – Mensagem ADT-A01
Para cada um dos eventos, se distinguem vários tipos de mensagens. A figura 8 mostra
alguns desses eventos.
Figura 8 – Exemplos de eventos da mensagem ADT
4.1.5.2.2 Pedidos/Query
Um dos exemplos típicos de uma mensagem de consulta de informação é a mensagem
RQI – Request for Patient Information como ilustrado na figura 9.
Figura 9 – Segmentos da mensagem de solicitação de informação de paciente
32
A mensagem representada na figura 10 mostra o pedido de informação (RQP^A19)
sobre o paciente cujo identificador é TR54.
Figura 10 – Mensagem HL7 para solicitação de informação de paciente
4.1.5.2.3 Resultados/Observações
A mensagem mais simples de resposta é a mensagem de confirmação (ACK Message).
A sua constituição é mostrada na figura 11, onde o segmento MSA permite especificar num
dos seus campos a confirmação/erro gerada como um dos seguintes valores: AA-Application
Accepted, AE-Application Error, AR-Application Rejected.
Figura 11 – Segmento resposta Confirmação
A figura 12 representa uma mensagem de confirmação de uma mensagem de admissão
no formato HL7.
Figura 12 – Mensagem HL7 ACK
Em resposta a uma mensagem RQI-Request for Patient Information (pedido), pode ser
enviada uma mensagem de resposta RPI- Response for Patient Information, cujos segmentos
podem ser observados na figura 13.
MSH|^~\&|GerenciaExames|150.162.67.45|CadastroPaciente||20051003091521||RQP^A19|IDMSH20051003 091521|P|2.3<cr> QRD|20051003 09:15:21|T|I|TR54|||2000|TR54|DEM|GE||T<cr> QRF|GerenciaExames|||||ANY|ANY|ALL|0800<cr>
MSH|^~\&|CadastroPaciente|ADT|CadastroPaciente|ADT|20051003 091411||ACK^|IDMSH20051003 091411|P|2.3<cr> MSA|AA|IDMSH20051003 091411<cr>
33
Figura 13 – Segmentos da resposta à solicitação de informação de paciente
A mensagem representada na figura 14 mostra o envio de uma mensagem RPI como
resposta a uma consulta. A mensagem deverá indicar que o paciente sofre de uma atrial
fibrillation.
Figura 14 – Mensagem HL7 resposta à solicitação de informação de paciente
Para a exemplicar a atualização de dados de paciente, como no recebimento de
resultado de um exame, é utilizada a mensagem do tipo PIN ^A08, como mostra a figura 15.
Figura 15 – Mensagem de atualização dados do paciente
São apresentados na figura 16 exemplos de segmentos que compõem o padrão HL7.
Embora seja fundamental a representação dos campos de cada segmento para a
implementação de uma interface que utilize o HL7, não é escopo deste trabalho detalhá-los.
MSH|^~\&|GerenciaExames|150.162.67.45|ProntuarioEletronico|IPConfirmacao|20051003 09:15:28|27013|PIN^A08|IDMSH20051003 09:15:28|P|2.3<cr> QRD|20051003 09:15:28|T|I|TR54|||2000|TR54|RES |GE||T<cr> DSP||Hemograma Completo (Leucocitos:[3] Eritrocitos:[2] Hematocitos:[1] Hemoglobina:[4] Colesterol:[5] Glicose:[6] Creatina:[7] Sodio:[8] Potassio:[9] Calcio:[1] Fosforo:[3])|<cr> DSP||Urina (PH:[1] Densidade:[2] Hemacias:[3] Proteinas:[4] Glicose:[5] Bacterioscopia:[44])|<cr>
MSH|^~\&|EKG||CDB||||RPI^R04|X981672|P<cr> MSA|AA|CDB22222<cr> QRD|198904180943|R|I|Q4412|||10|PATID5239|0123456-1|RES<cr> OBR||||93000^EKG REPORT|||198801111330<cr> OBX|1|CE|8601-7^EKG IMPRESSION:^LN|1|^ATRIAL FIBRILATION||||||F<cr>
34
Figura 16 – Principais segmentos do padrão HL7
4.1.6 Quem utiliza HL7?
A adoção de padrões, principalmente aqueles reconhecidos internacionalmente cria a
possibilidade de integração de sistemas em diversos níveis como municipal, estadual, federal
e entre instituições prestadoras de serviço.
Como o padrão HL7 é submetido a aprovação de uma organização internacional
(ANSI), a aceitação e o interesse dos países pelo HL7 vem aumentado constantemente.
35
Acredita-se que esse fato ocorra e tende a crescer devido à necessidade da melhoria dos
serviços clínicos e hospitalares a um baixo custo oferecido pelo HL7.
No Brasil, entre os centros de saúde que contam com o sistema HL7, encontra-se o
Hospital Italiano (Grajaú/RJ) e algumas companhias pré-pagas que já estão trabalhando com
parte do padrão HL7 para o envio de mensagens, onde podemos citar algumas fornecedoras
de planos de saúde (MARCH, 2002) que operam em São Paulo e outras cidades do Brasil
como: Omint, Marítima Seguro Saúde, Central Nacional Unimed - Cooperativa Central, Sul
América Aetna Saúde Seguros S/A, Porto Seguro Saúde S/A. (SALUTIA, 2005). Ainda é
possível citar centros de pesquisa brasileiros, interessados nas funcionalidades fornecidas pelo
protocolo HL7 assim como o projeto SIDI da Universidade Federal do Rio Grande do Sul em
parceria com a Escola Paulista de Medicina.
A nível internacional, pode-se observar:
• Alta implantação: Estados Unidos, Alemanha, Reino Unido, Holanda, Japão,
Canadá, Austrália, México, Índia e Argentina;
• Iniciativas regionais: Finlândia, Croácia, Lituânia, Suíça, Dinamarca e Coréia;
• Pequenos desenvolvimentos: Espanha, Itália, França, Grécia e Venezuela.
4.2 MIDDLEWARE �
O termo middleware é utilizado em uma série de situações, nos mais diversos
contextos e com os mais diversos objetivos. Inicialmente era visto como uma camada de
tradução ou comunicação, localizada entre as aplicações e o sistema operacional, atualmente
abrange a concepção de uma biblioteca de suporte a execuções de chamadas remotas.
(MACÊDO, 2004).
“Na Enciclopédia de Computação Distribuída, Bakken define middleware como uma classe de tecnologia de software projetada para gerenciar a complexidade e a heterogeneidade inerente aos sistemas distribuídos. Talarian Corporation caracteriza middleware como o software que é usado para mover informação de um programa para um ou mais programas, protegendo o desenvolvedor de dependências do protocolo de comunicação, sistemas operacionais e hardware. Campbell, por sua vez, afirma que middleware é qualquer camada de software que se situa acima da infra-estrutura de sistemas distribuídos e abaixo da camada da aplicação”. (COSTA, 2004).
36
Assim, de modo geral, conclui-se que o middleware é uma camada de software que
possui como finalidade disponibilizar serviços que facilitem o trabalho do desenvolvedor.
Esses serviços podem estar relacionados a diversos requisitos de aplicações: distribuição,
armazenamento, tolerância a falhas, balanceamento de carga, dentre outras. (MACÊDO,
2004).
Uma camada de software disponibilizada pelo middleware realiza todo o
procedimento de empacotamento, envio e desempacotamento de requisições via canal de
comunicação utilizando serviços como troca de mensagens, isto é, o middleware fornece um
nível de abstração intermediário entre o ambiente de execução e a aplicação, tornando
transparente ao desenvolvedor da aplicação detalhes do ambiente, como de localização de
serviços, linguagens de programação, heterogeneidade de plataformas e hardware usados, etc.
(MACÊDO, 2004).
Um tipo de middleware freqüentemente utilizado é o orientado a mensagens (Message
Oriented Middleware - MOM). Neste contexto, a função do middleware se aproxima ao
trabalho de um repositório, onde mensagens podem ser armazenadas e recuperadas,
fornecendo assim, um mecanismo para comunicação assíncrona entre aplicações, utilizando
para isso APIs como os sockets. Os principais produtos que seguem a linha dos MOM’s são o
MQSeries da IBM, o MSMQ da Microsoft e o SmartSockets da Talarian. (MACÊDO, 2004).
A chamada de procedimentos remotos (Remote Procedure Call - RPC), desenvolvida
pela Sun Microsystems, em 1982, foi uma das primeiras tentativas de definição de um
middleware para oferecer mecanismos de abstração a fim de gerenciar a complexidade do
desenvolvimento de sistemas distribuídos. Esse tipo de middleware apresenta uma série de
ferramentas e definições que possibilitam a execução de procedimentos remotos com a
mesma simplicidade e facilidade da execução de procedimentos locais. (MACÊDO, 2004).
A evolução do middleware baseado em RPC foi o desenvolvimento do ORB (Object
Request Broker), acrescentando as vantagens da orientação a objetos na chamada de
execuções remotas. Neste, além das questões de comunicação em rede, também são
disponibilizados serviços como segurança, controle de acesso, transações distribuídas e
balanceamento de carga. São exemplos, deste tipo de middleware, o CORBA da OMG,
DCOM da Microsoft, EJB da Sun, e o TAO (The Ace ORB). (MACÊDO, 2004).
Apesar do baixo nível de abstração e da necessidade de se conhecer aspectos de redes
de computadores como portas e números IPs (GUEDES, 2004), utilizamos um middleware
37
orientado a mensagens para a construção do middleware HL7. Este tipo de middleware foi
selecionado visto que o uso de sockets é permitido na maioria das linguagens de programação
existentes, e também porque o cliente desenvolvido deverá executar em uma máquina local.
O uso de middleware é adequado quando é buscado um padrão para
interoperabilidade, escalabilidade e gerência da complexidade através da utilização de
abstrações e serviços. Assim, a finalidade do projeto de middleware é terceirizar a construção
de serviços, tratar a heterogeneidade e ocultar complexidade. Proporcionando, dessa forma, o
desenvolvimento de aplicações flexíveis, modulares e confiáveis. Este é o objetivo central do
middleware desenvolvido neste trabalho.
4.3 PROGRAMAÇÃO PARALELA E DISTRIBUÍDA
Um programa paralelo é um programa que define ações que podem ser executadas
simultaneamente, ou seja, contém dois ou mais processos que trabalham juntos para executar
uma determinada tarefa, onde cada processo é um programa seqüencial. (MONGELLI, 2003).
Os processos em um programa paralelo trabalham juntos comunicando-se entre si. A
comunicação pode ser feita através de variáveis compartilhadas ou por troca de mensagens.
Mas, independente da forma de comunicação os processos precisam sincronizar-se, seja
através de exclusão mútua (região critica) ou sincronização condicional. Para a exclusão
mútua existe um mecanismo denominado mutex que permite o acesso único aos dados e ou
recursos, criando filas quando os mesmos estiverem bloqueados. (MONGELLI, 2003).
Já um programa distribuído é aquele em que um conjunto de processos seqüenciais
executa em paralelo e, comunicam-se através de passagem de mensagens (primitivas send e
receive) em uma rede de computadores. No entanto, um programa distribuído também pode
ser executado em um sistema de multiprocessadores com memória compartilhada, ou até
mesmo em um sistema monoprocessador. (MAZZUCO, 1999).
A computação distribuída propõe o modelo de um sistema utilizando o paradigma
cliente-servidor, onde a aplicação que assume o papel de servidor aguarda mensagens,
executa serviços e retorna resultados, enquanto que a aplicação cliente, é aquela que
estabelece a conexão, envia mensagens para o servidor e aguarda mensagens de
resposta.(WIKIPEDIA).
38
Para que a transmissão de mensagens possa ser efetuada, vários fatores devem ser
considerados, por exemplo, quando se envia uma mensagem é fundamental saber quem está
enviando (emissor), para quem está sendo enviada (destino) e o que está sendo enviado (tipo).
Além disso, pode-se ter garantia, de que a mensagem chegou ao seu destino, que a mesma
seja aceita pelo receptor remoto, que haja alguma forma de confirmação, que haja algum
tratamento de exceção, entre outros. Já para o recebimento de uma mensagem, é necessário
saber para qual, ou quais processos a mensagem que acaba de chegar é destinada, se deve ser
criado um processo especial para a manipulação dessa mensagem, se a mensagem é enviada a
um processo existente e que está ocupado, será ela enfileirada ou simplesmente descartada.
(MAZZUCO, 1999).
Para promover a transmissão de mensagens é recomendado o uso de API (Application
Programming Interface) de comunicação. As APIs fornecem primitivas de comunicação que
podem ser chamadas a partir do código, para que o acesso aos serviços de comunicação possa
ser utilizado pelas aplicações. As principais APIs de comunicação são: (SIQUEIRA)
• Sockets: portas de comunicação locais ou de rede (versão segura SSL);
• Suportes de RPC (Remote Procedure Call): permitem chamar métodos remotamente
(exemplos: Java RMI, Sun RPC, ...);
• Canais de eventos: permitem notificar threads e processos dos eventos ocorridos no
sistema (exemplos: JMS, CORBA Notification Service, …).
O socket é uma abstração desenvolvida pela Universidade Berkeley proposta
originalmente para o sistema operacional Unix BSD (por isso, também conhecidos como
sockets Berkeley), que estabelece um conjunto de interfaces para uma aplicação acessar os
protocolos da camada de transporte do modelo OSI (vide figura 1). Muitos fabricantes de
computadores e sistemas operacionais como SUN, COMPAQ, Tektronic, adotaram a interface
sockets Berkeley. A Microsoft também optou pelo socket projetando uma interface
denominada winsock. Basicamente, a API sockets é constituída por constantes, estruturas e
funções que são chamadas em uma seqüência adequada definindo algoritmos genéricos para
aplicações cliente-servidor. (GUEDES, 2001).
Os sockets representam uma porta de um canal de comunicação associada a uma
aplicação. São identificados por um inteiro de 16 bits (0 a 65535), onde os valores de 0 a 1024
39
são reservados para os serviços padronizados pela rede, os demais valores podem ser
utilizados livremente pelos desenvolvedores. (SIQUEIRA).
Os sockets podem ser classificados segundo o tipo de protocolo de transporte
empregado durante a transmissão de mensagens:
• Sockets Stream: utilizam o protocolo TCP (transporte confiável, isto é, verifica se os
dados são enviados de forma correta, na seqüência apropriada e sem erros; e em modo
orientado à conexão, ou seja, a aplicação envia um pedido de conexão para o destino e
usa esta conexão para transferir dados);
• Sockets Datagrama: utilizam o protocolo UDP (transporte não confiável, ou seja, não
garantem que os dados chegaram ao destino corretamente, e em modo não orientado à
conexão);
• Sockets Multicast: envio simultâneo de uma mensagem a um grupo de destinatários
sem estabelecimento de conexão.
4.3.1 C++
A linguagem de programação C++ é, na verdade um superconjunto da linguagem de
programação C. A linguagem C foi desenvolvida em 1972, por Dennis Ritchie e Ken
Thompson criaram a Linguagem C para aumentar o poder de sua antecessora a linguagem B.
O C demorou a se popularizar e apenas em 1978 com a publicação do livro “The C
Programming Language” de Bjarne Stroustrup pelos criadores de C e o lançamento do IBM
PC, em 1981, é que houve maior interesse por parte dos desenvolvedores. Assim que o
número de PC’s aumentava, também aumentava o número de programas desenvolvidos em C.
(MACIEL, 2004).
A tarefa de manutenção e compreensão do código fonte gerado tornou-se importante e
altamente complexa, visto que C utiliza programação estruturada, permitindo assim a total
liberdade aos desenvolvedores durante a implementação. Deste modo, cada desenvolvedor
implementava de forma diferenciada seus aplicativos, assim quando outro desenvolvedor via
a necessidade de manutenção havia grandes dificuldades para a compreensão do código.
A solução para este problema seria o uso da programação orientada a objetos, assim
surgiu a linguagem C++, projetada por Bjarne Stroustrup, inspirado em parte por outras
40
linguagens, como o Simula67 e Smalltalk, consideradas como algumas das mais puras
linguagens Orientadas a Objetos. (MACIEL, 2004).
No início da década de 80 não havia documentação de projeto de C++ e nem comitê
para aprovação dos padrões C++. Já em 1987, com a alta aceitação de C++ pelos usuários,
viu-se a necessidade de uma padronização formal da linguagem. Somente em 1995, foi
divulgado um projeto de padrão inicial para revisão pública e em 1998 um padrão
internacional formalmente aprovado para C++. (MACIEL, 2004).
4.3.1.1 Por que usar C++?
Optou-se pelo uso de C++: pela sua compatibilidade com os sistemas operacionais
Windows e Unix, pelo fato de ser uma linguagem de programação gratuita, por apresentar
maior velocidade de execução quando comparado a outras linguagens como Java e, por
último, ao contrário ao C, porque C++ é uma linguagem orientada a objetos.
Podemos descrever como principais vantagens do C++:
• Abstração de Dados: combina os dados e as funções usadas para manipulá-los,
de tal forma que os detalhes da implementação fiquem ocultos para outros
desenvolvedores, possibilitando o desenvolvimento de programas mais fáceis
de manter e de aprimorar;
• Orientada a Objeto: permite tornar um código existente facilmente
modificável sem, na realidade, alterar fisicamente o código;
• Programação Genérica: refere-se a um tipo de abstração no processo de
desenvolvimento de software, voltado para a construção de algoritmos que
independem de um determinado tipo ou estrutura de dados e que, no entanto,
sejam tão eficientes quanto se construídos acoplados a uma determinada
estrutura. A biblioteca STL ( Standard Template Library) de C++ utiliza a
programação genérica. (ARNAUT).
4.4 G.A.L.S.
O G.A.L.S.(Gerador de Analisadores Léxicos e Sintáticos) é um ambiente de
desenvolvimento destinado à geração de analisadores léxicos e sintáticos de uma gramática.
41
Foi desenvolvido em 2002 por Carlos Eduardo Gesser como trabalho de conclusão de curso
de Ciências da Computação, da Universidade Federal de Santa Catarina. (GESSER, 2002).
Um analisador léxico pode ser definido como um processo usado para analisar a
entrada de linha de caracteres, produzindo assim uma seqüência de símbolos chamados tokens
que podem ser manipulados mais facilmente por um leitor de saída, o parser.
Para facilitar o entendimento cita-se como exemplo, a análise de uma palavra, onde se
verifica, através da análise léxica, se existe ou não algum caractere que faz parte do alfabeto
da língua portuguesa. Assim, através da análise léxica da palavra “casa” pode-se afirmar que
não há caracteres na sentença que não pertençam ao nosso alfabeto, o mesmo não ocorre, por
exemplo, com a sentença ca$a, pois o símbolo “$” não pertence ao nosso alfabeto.
Partindo deste princípio pode-se construir um alfabeto e através da análise léxica,
testar se um token apresentado como entrada faz parte deste alfabeto.
Já o analisador sintático verifica recursivamente se uma combinação de tokens
pertence à gramática, utilizando para isto, um conjunto de regras previamente estabelecidas.
O GALS permite configurar aspectos dos analisadores (léxico e sintático) gerando
assim o Analisador Léxico, Analisador Sintático e ainda um Analisador Semântico vazio, para
a implementação do usuário. O GALS também permite a seleção da linguagem em que os
analisadores deverão ser gerados como Java, C++ e Delphi.
A figura 17 mostra a interface do aplicativo utilizado para a construção dos
analisadores léxicos e sintáticos.
Figura 17 – Geração dos analisadores léxico e sintático
42
Existem diversas ferramentas para a geração de analisadores léxico e sintático, como
por exemplo, LEX, GALEX, GAS e LISA. Uma ferramenta semelhante ao GALS e que
também executa as mesmas funcionalidades, é a ferramenta LISA. Ambas utilizam
convenções do padrão BNF(Backus Normal Form): os símbolos não-terminais são escritos em
letras maiúsculas. Os demais símbolos são terminais, com exceção dos meta símbolos: | . [ ] {
} < > . Se o terminal definido contiver qualquer meta símbolo, este deverá estar entre <>. O
terminal * é representado com < >. (GUIMARÃES).
43
5 SISTEMAS MODELOS
Com o objetivo de proporcionar o intercâmbio de informações entre sistemas clínicos
e hospitalares utilizando padrão de comunicação HL7, foram desenvolvidos dois modelos
com arquiteturas distintas, porém com a mesma meta: permitir a interoperabilidade entre
sistemas heterogêneos na área de saúde.
O primeiro modelo desenvolvido será aqui chamado de modelo especializado, devido
sua arquitetura cliente-servidor ser mais dependente da aplicação. O segundo modelo a ser
implementado será identificado como modelo Genérico pelo fato deste, contrário ao modelo
anterior, não apresentar dependência quanto à aplicação.
Para um melhor entendimento das estruturas e seus relacionamentos nos modelos aqui
propostos, primeiramente será necessário conhecer a gramática gerada para o
desenvolvimento dos sistemas modelos, bem como seus requisitos, arquiteturas e
funcionalidades.
5.1 GRAMÁTICA
Antes de descrevermos a gramática usada para a geração dos modelos desenvolvidos,
torna-se necessário conhecimento de alguns conceitos, utilizados para a geração da gramática,
como autômatos e linguagem.
O autômato ou autômato finito é um modelo matemático de um sistema, com entradas
e saídas discretas cujo principal interesse é sua capacidade de especificar finitamente
linguagens (ou procedimentos) infinitos. Sendo assim, autômato finito sempre poderá
determinar um conjunto de strings, considerando parte do conjunto as seqüências de entrada
que levarão o autômato a um estado de aceitação e rejeitando todas as outras. (GULIATO,
CARNIELLI, ZUFFO).
Existem vários motivos para se estudar a teoria dos autômatos. Em muitos casos eles
constituem um modelo útil para muitos elementos importantes de hardware e software. Como
exemplo de sua importância podemos citar dois exemplos de seu emprego:
44
• Procura e reconhecimento de padrões simbólicos: análise de grandes corpos de
textos, como coleções de páginas da web, a fim de encontrar ocorrências de
palavras, frases ou outros padrões;
• Analisador léxico: divisão do texto de entrada em unidades lógicas, como
identificadores, palavras-chave e pontuação. (ARAÚJO, et al.)
O segundo exemplo foi utilizado para o desenvolvimento dos sistemas modelos afim
de que haja o reconhecimento dos caracteres que compõem uma determinada string
(mensagem), seguindo assim as normas de especificação do HL7. Através da figura 18 é
possível observar os tokens (caracteres) aceitos pela análise léxica em uma mensagem no
formato HL7.
Uma gramática serve para definir qual o subconjunto de sentenças que faz parte de
uma determinada linguagem. Ela é um dispositivo formal para especificar uma linguagem
potencialmente infinita de uma forma finita. Pode-se dizer ainda que uma gramática é um
conjunto de leis de formação de uma cadeia, isto é, dada uma gramática é possível aplicarmos
suas leis de formação até que determinada string seja obtida ou derivada da gramática.
(MELLO, LACERDA,2001).
45
Figu
ra 1
8 –
Tab
ela
da A
nális
e L
éxic
a
46
As gramáticas foram divididas em quatro classes, capazes de gerar classes
correspondentes de linguagens, de acordo com a Hierarquia de Chomsky:
• GEF (gramática com estrutura de frase ou Tipo 0): não apresenta restrições
afora as próprias condições descritas para a existência de uma gramática;
• GSC (gramática sensíveis a contexto ou Tipo 1): possui uma restrição com
relação as leis de formação: dada uma lei de formação a�B, o tamanho de “a”
deve ser menor ou igual ao tamanho de B;
• GLC (gramática livre de contexto ou Tipo 2): uma gramática livre de contexto
é uma gramática sensível a contexto com uma restrição a mais: as produções
devem ser da forma A�a, onde A é um elemento não terminal, ou seja, o lado
esquerdo das produções devem conter sempre um, e apenas um, elemento não
terminal. Outra forma de se representar gramáticas livres de contexto é através
da forma normal de Backus (BNF). Neste caso, o símbolo � é substituído por
::= e os símbolos não terminais são representados entre <>;
• GR (gramática regular ou Tipo 3): uma gramática regular também é uma
gramática livre de contexto com restrições a mais: as produções devem
obedecer a forma A�aB ou A�b, onde A e B são símbolos não terminais, “a”
é um símbolo terminal e “b” é um símbolo terminal ou cadeia vazia. Assim,
pode-se resumir que, em todas as produções a cadeia da direita deve conter até
dois símbolos e nunca um símbolo terminal deve proceder a um terminal.
Uma vez compreendido o significado de cada termo da especificação do padrão HL7,
o passo seguinte envolveu a construção da gramática que represente a troca de mensagens no
padrão HL7. Devido à familiaridade e eficiência, foi utilizado o aplicativo G.A.L.S. (Gerador
de Analisadores Léxicos e Sintáticos), para a geração da gramática.
Já que a implementação estava baseada na versão 2.3 do HL7, onde existem diversas
flexibilidades, foi decidido especificar a gramática seguindo a especificação proposta pela
versão, ou seja, os campos de cada seguimento foram especificados na totalidade, mesmo que
mais tarde se constate que não poderão ser preenchidos pela aplicação.
O tipo de gramática desenvolvida na implementação dos modelos foi à gramática livre
de contexto recomendada para este tipo de aplicação: reconhecimento de tokens. Na figura 19,
é possível ser observado a especificação do campo MSH, especificado pelo HL7, utilizado nas
47
composições das mensagens. Além do MSH também foram utilizadas as especificações dos
seguintes campos nos respectivos modelos:
• Modelo especializado: MSH, MSA, PID, EVN;
• Modelo genérico: MSH, MSA, PID, EVN, QAK, DSP, QRD, QRF.
Figura 19 – Gramática referente ao campo MSH
A implementação dos demais campos poderá ser observada no Anexo 2.
Como a gramática do padrão HL7 pode ser representada através de uma gramática
livre de contexto, a classe do analisador escolhido para a implementação foi do tipo preditivo
(LL1), utilizando o modelo descendente (top-down), por isso, para a gramática gerar um
autômato determinístico, foi necessário o cumprimento de três requisitos:
1) Não poderá possuir recursão à esquerda;
2) Deverá estar fatorada;
3) Não poderá ser ambígua.
48
5.2 ARQUITETURA
5.2.1 Modelo Especializado
O primeiro modelo desenvolvido, caracterizado por apresentar uma arquitetura mais
simples, porém dependente da aplicação, ou seja, trata de uma arquitetura cliente-servidor em
que a aplicação deverá ser responsável pela implementação do padrão HL7. Na figura 20 é
possível a análise de sua estrutura.
Figura 20 – Arquitetura do Modelo Especializado
As aplicações (Aplicação A e Aplicação B) dizem respeito aos softwares presentes em
um ambiente clínico e/ou hospitalar. Ao nível de teste foram desenvolvidas duas aplicações
responsáveis pelo registro de dados demográficos de pacientes (admissão de pacientes) em
que a comunicação entre elas envolvia troca de mensagens referentes a uma inclusão de
paciente.
O sistema Servidor, representado na figura 21, é responsável pelo encaminhamento de
mensagens, e apresenta em sua estrutura, três tabelas com as seguintes especificações:
Figura 21 – Estrutura do servidor (tabelas base de dados)
49
• Configurações: tabela responsável pelo registro das aplicações presentes na
rede. Apresenta dados como: porta, IP e nome da aplicação, como mostrado na
figura 22.
Figura 22 – Tabela de Configurações do servidor
• Caixa de Saída: tabela que contém informações das mensagens que por algum
motivo não foram encaminhadas aos seus destinatários com sucesso. A figura
23 mostra esta tabela, que é composta pelos campos: IP da aplicação destino,
IP da aplicação origem, porta da aplicação, mensagem, ID da Mensagem, data
da mensagem.
Figura 23 – Tabela de Caixa de Saída
• Erros: tabela responsável pelo armazenamento dos erros ocorridos durante a
transmissão de mensagens. Campos: Tipo erro (contém a mensagem de erro
retornada), data. A figura 24 mostra a Tabela de Erros, contendo uma
mensagem que excedeu o tempo de tentativas de envio.
Figura 24 – Tabela de Erros
Vale ressaltar que neste modelo, todas as trocas de mensagem deverão estar no
formato do padrão de comunicação HL7.
5.2.2 Modelo Genérico
Após a implementação do Modelo Especializado, verificou-se a possibilidade de
integrar aplicações sem a necessidade de estas implementarem o padrão HL7, o que acabou
50
levando a geração de um novo modelo que possibilitasse a comunicação entre variadas
aplicações sem muita alteração em sua estrutura, sendo apenas necessário que estas sejam
capazes de enviar streams ou arquivos XML seguindo uma estrutura mais simples que a
proposta pelo HL7.
O Modelo Genérico apresenta comportamento semelhante de um middleware, uma
vez que este modelo nada mais é que uma camada de software que possui como finalidade
disponibilizar serviços que facilitem o trabalho do desenvolvedor.
Através da figura 25 é possível compreender a estrutura deste modelo:
Figura 25 – Arquitetura do Modelo Genérico
As aplicações GE, CP e PEP tratam respectivamente de aplicações referentes à
gerência de exames laboratoriais, cadastro de pacientes e prontuário eletrônico de paciente.
Vale lembrar que tais aplicações foram desenvolvidas apenas ao nível de teste possibilitando a
análise da execução do modelo construído. A seguir, segue as especificações das aplicações
desenvolvidas a fim de que seja possível a compreensão do sistema como um todo:
• GE (Gerência de Exames): aplicação responsável pelo cadastro e consulta de
exames. Quando um novo registro de exame é armazenado na base de dados
desta aplicação, deverá ser verificado se o paciente responsável pelo novo
exame encontra-se registrado na base de dados da aplicação referente a
cadastro de pacientes (aplicação CP). Caso esteja, os dados do novo exame
deverão ser enviados para o aplicação Prontuário Eletrônico do Paciente (PEP);
• CP (Cadastro de Pacientes): aplicação responsável pela admissão de novos
pacientes no sistema. Sempre que houver um novo cadastro de paciente, esta
51
aplicação encaminhará os dados demográficos do novo paciente para as
aplicações cujos dados de paciente são relevantes. No nosso exemplo, os
aplicações interessados são as aplicações GE e PEP;
• PEP (Prontuário Eletrônico de Paciente): aplicação responsável pelo histórico e
evolução do paciente. A base de dados desta aplicação é alimentada pelas
aplicações GE e CP.
Para garantir a afirmação de que o modelo desenvolvido apresenta independência de
linguagem, ou seja, é possível permitir a comunicação entre as aplicações independentes da
linguagem em que foram desenvolvidas, as aplicações PEP e CP foram implementadas em
Delphi enquanto que o GE foi implementado em C++.
A estrutura do servidor é a mesma apresentada pelo Modelo Especializado, formado
portanto, pelas tabelas Configurações, Caixa de Saída e Erros.
A troca de mensagens entre as aplicações e o sistema cliente deverá estar especificada
em um formato pré-estabelecido, em uma estrutura simples, diferente do HL7. Já a troca de
mensagens entre os sistemas cliente e servidor devem ser geradas seguindo a estrutura
proposta pelo padrão de comunicação HL7.
5.3 FUNCIONALIDADES
5.3.1 Modelo Especializado
Para uma maior compreensão das funcionalidades deste modelo, suponha que a
Aplicação A, responsável pelo cadastro de pacientes, deseja informar às demais aplicações
sobre uma nova admissão. Para isso a Aplicação A deverá construir uma mensagem de
admissão no formato HL7, contendo os dados demográficos do paciente e encaminhar a
mensagem ao servidor. O servidor ao receber uma nova mensagem verifica o tipo desta (ADT
ou ACK), sendo que caso seja uma mensagem do tipo ADT (admissão de paciente), a
mensagem recebida deverá então ser encaminhada a todos as aplicações registrados na tabela
de Configurações, porém caso tipo de mensagem seja ACK (confirmação), servidor
encaminha mensagem apenas ao destinatário registrado na mensagem.
52
Se destinatário não estiver registrado na tabela de Configurações do servidor, o
servidor encaminha a mensagem de erro ao remetente da mensagem e armazena os dados em
sua tabela de Erros.
Caso, por algum motivo, não seja possível enviar mensagem ao seu destinatário, o
servidor armazena a mensagem na tabela referente a Caixa de Saída para posteriormente
tentar reenviar a mensagem.
A aplicação destino ao receber uma nova mensagem, também deverá verificar o tipo
de mensagem, uma vez que caso a mensagem seja de admissão, a aplicação deverá armazenar
as informações do novo paciente em sua base de dados e enviar uma mensagem de
confirmação ao remetente através do servidor.
Para que todos os procedimentos descritos a cima ocorram com sucesso, algumas
observações deverão ser levadas em consideração:
• As mensagens devem ser construídas corretamente em função da gramática
desenvolvida;
• As mensagens recebidas devem ser analisadas se foram construídas
corretamente;
• O envio de mensagens deverá ocorrer via socket através de uma conexão
TCP/IP;
• Todas as aplicações do sistema devem estar registradas na tabela de
Configurações do Servidor e apresentarem dados consistentes;
• De tempos em tempos servidor verifica se há mensagens em sua caixa de saída,
caso tenha, deverá ser verificado se a mensagem não se encontra expirada
(armazenada, por exemplo, na caixa de saída a mais de 24 horas) e em caso
afirmativo, a mensagem expirada deverá ser incluída na tabela de Erros do
servidor e excluída da tabela caixa de saída.
5.3.2 Modelo Genérico
Para proporcionar a compreensão das funcionalidades presentes neste modelo, será
demonstrado o processo de inclusão de paciente e de exames.
53
5.3.2.1 Inclusão de Pacientes
O processo de admissão de pacientes inicia a partir do aplicação Cadastro de Paciente,
sendo também de interesse às aplicações Gerência de Exames e Prontuário Eletrônico de
Paciente. Sabendo que o formato das mensagens trocadas entre as aplicações e seus
respectivos clientes difere do formato das mensagens trocadas entre os sistemas Cliente e
Servidor, foi preciso adotar uma estrutura de mensagem muito mais simples que o HL7, o que
acaba garantindo desta forma a independência do sistema com relação às aplicações.
Caso a aplicação CP deseje enviar mensagem de admissão para as aplicações GE e
PEP, ela deverá enviar as informações demográficas do novo paciente, bem como o nome da
aplicação destino e o tipo de mensagem. A informação referente à aplicação destino é válida
apenas nos casos em que o tipo de mensagem a ser enviada é uma confirmação, solicitação
de busca ou resultado de busca, nos casos em que o tipo de mensagem é referente a uma
admissão, tal informação deverá ser preenchida apenas de forma representativa, uma vez que
este tipo de mensagem é encaminhada pelo servidor para todas as aplicações registradas em
sua base de dados.
As informações enviadas pela aplicação ao seu respectivo cliente deverão estar
separadas por um símbolo pré-definido, em nosso exemplo utilizamos o símbolo “|” como
separador.
A partir do momento que o cliente receber a mensagem da aplicação, esta deverá ser
reconstruída seguindo as normas de especificação do padrão HL7. Na figura 26, pode-se
verificar uma mensagem referente a admissão de paciente enviada pela aplicação para seu
cliente.
Figura 26 – Mensagens Trocadas Entre Aplicação e Cliente
A figura 27 representa uma mensagem de admissão de paciente que foi construída com
os dados recebidos pela aplicação e será enviada para o servidor conforme o padrão HL7.
54
Figura 27 – Mensagens Trocadas Entre Cliente x Servidor
Os passos seguintes são semelhantes aos passos descritos no modelo anterior, porém
quando da chegada da mensagem ao servidor, este adiciona na mensagem a ser encaminhada
a informação referente à porta da aplicação destino. Com esta informação o sistema cliente da
aplicação destino saberá a qual aplicação a mensagem deverá ser encaminhada, lembrando
ainda que o formato da mensagem encaminhada para a aplicação terá uma estrutura diferente
da estrutura proposta pelo HL7.
5.3.2.1 Inclusão de Exames
O processo de envio de mensagens e as suas estruturas são as mesmas especificadas no
processo de inclusão de paciente. O que difere uma ação da outra é que o processo de inclusão
de exames também envolve mensagens do tipo busca e resultado de busca.
Quando ocorre uma inclusão de exames pela aplicação GE, a mesma verifica se o
paciente responsável pelo exame já se encontra registrado na aplicação Cadastro Paciente
através da flag tem_cadastro_paciente na tabela Paciente de GE. Caso a flag esteja setada com
“S”, o sistema encaminha uma mensagem de atualização dos dados do paciente, informando o
novo exame à aplicação Prontuário Eletrônico do Paciente. Caso a flag esteja setada com “N”,
a aplicação GE encaminha uma mensagem de busca à aplicação CP e armazena os dados da
busca em sua tabela Busca, para caso hajam mais exames cadastrados para o mesmo paciente,
o sistema não tenha que encaminhar nova mensagem de busca por paciente, uma vez que esta
já foi recebida pela aplicação GE para o mesmo paciente.
Cadastro Paciente ao receber uma mensagem de busca, retorna ao remetente a
mensagem contendo o resultado da busca solicitada, nesta mesma mensagem também deverá
conter o status do resultado da busca, que deverá ser identificada pela string “NF” caso não
haja nenhum registro referente a busca ou “OK”, caso haja registro para a busca solicitada.
55
GE ao receber mensagem contendo resultado da busca de um determinado paciente
verifica o status da busca e caso este contenha o valor “OK”, a aplicação GE exclui a busca de
sua tabela Busca, seta a flag tem_cadastro_paciente da tabela paciente para “S” e encaminha
ao PEP uma mensagem referente à atualização do paciente contendo o novo exame realizado.
A figura 28 mostra uma mensagem no formato HL7 para a solicitação de dados do
paciente cujo ID é 1113.
Figura 28 – Mensagem de solicitação de dados de paciente
A figura 29 representa a mensagem HL7 que retorna os dados referentes a uma
solicitação de busca, neste caso, a mensagem será enviada ao solicitante (GerenciaExames) os
dados cadastrais do paciente que lhe foi solicitado (1113).
Figura 29 – Mensagem de resposta à solicitação de dados de paciente
5.4 MODELO ESPECIALIZADO X MODELO GENÉRICO
A vantagem do Modelo Genérico com relação ao Modelo Especializado é a
possibilidade de integração de sistemas sem que as aplicações sofram grandes alterações em
sua estrutura. Recomenda-se a implementação o protocolo HL7 junto às aplicações, ou seja,
toda a aplicação deve implementar o padrão HL7, porém quando já se faz uso de aplicações e
deseja-se a comunicação entre as mesmas? Sabe-se que para este caso, dependendo da
aplicação, a implementação do padrão HL7 poderia vir a ser inviável e sem falar do alto
investimento necessário para sua implantação. Assim através do Modelo Genérico as
56
aplicações não necessitam implementar o padrão de comunicação HL7, sendo assim apenas
necessário que estas apresentem estruturas que suportem sockets com conexão TCP/IP.
Já a desvantagem do Modelo Genérico está em seu desempenho que poderá sofrer uma
queda quando comparado ao Modelo Especializado, visto que agora o Modelo Genérico
deverá primeiramente repassar as informações necessárias para o cliente para então a
mensagem no formato do padrão HL7 ser gerada.
57
6 CONCLUSÃO Compreendida a necessidade da informatização na área de saúde, onde disponibilizar
informações de pacientes de forma eletrônica, facilita seu gerenciamento, permite melhor
atendimento médico, maior qualidade de serviços e, ainda possibilita redução de custos.
Porém, para estas informações possam ser acessadas, verificou-se que é imprescindível o uso
de padrões de comunicação como o HL7, que permite uma integração entre sistemas de forma
flexível e transparente.
A construção da arquitetura cliente-servidor no primeiro modelo mostrou-se muito
mais complicada do que se esperava, pois como partimos do princípio de utilizar um ambiente
de desenvolvimento gratuito, e que contemplasse multiplataforma, a tecnologia não permitia
flexibilidade quanto ao uso de sockets e threads, como é comum em outras linguagens.
Outra dificuldade observada, foi devido à especificação um tanto complexa da
gramática, ocasionando maior tempo de implementação e teste para o uso correto da estrutura
proposta pelo HL7. Provavelmente este problema será evitado quando usada a versão 3 do
padrão HL7.
Como a tarefa de implementação do padrão HL7 se mostrou demasiadamente
exaustiva, sentimos a necessidade do desenvolvimento de um middleware para facilitar o
trabalho do desenvolvedor. Dessa forma, o desenvolvedor poderia integrar seus sistemas, sem
tomar conhecimento da complexidade que envolve a implementação das especificações do
padrão HL7.
Ao final deste projeto, construimos modelos de sistemas que podem ser empregados
para a solução de problemas distintos: o Modelo Especializado repassa ao desenvolvedor, a
estrutura necessária para o desenvolvimento de novas aplicações capacitadas a efetuar troca
de informações com o uso do padrão HL7; enquanto que o Modelo Genérico, através de um
middleware HL7, atende a necessidade de se obter integração com aplicações já existentes.
Depois de superados todos os obstáculos, verificamos que o esforço necessário para o
desenvolvimento destes modelos foi compensador. Os benefícios oferecidos pelo padrão HL7
foram alcançados, promovendo desta forma a distribuição das informações ditas essenciais
em um ambiente de saúde.
58
7 TRABALHOS FUTUROS
• A estrutura das mensagens trocadas entre aplicações e clientes poderia ser implementada
utilizando o padrão XML.A utilização do XML permitiria maior facilidade de integração
do padrão HL7 quando utilizada sua nova versão( HL7 versão 3).
• Criação de uma API evitaria que o desenvolvedor da aplicação distribuída tivesse que
implementar uma grande parte dos serviços não-funcionais de que necessita. Utilizando
uma API, tudo que o desenvolvedor teria que fazer seria escolher que serviços ele iria
utilizar.
• Uso do middleware desenvolvido em aplicações reais.
59
REFERÊNCIAS ARAÚJO, et al. Automatas, Introdução. Disponível em: <http://www.ele.ita.br/~nogueira/ introducao.htm>. Acesso em: 20 set. 2005. ARNAUT, Dagoberto Haele. Entendendo C++. Disponível em: <http://www.arnaut.eti.br/op /CPPAI01.htm>. Acesso em: 20 set. 2005.
BRASIL, Comitê Executivo de Governo Eletrônico. e PING - Padrões de Interoperabilidade de Governo Eletrônico. Documento de Referência. Versão 1.0. Julho de 2005. Disponível em: <http://www.governoeletronico.gov.br/governoeletronico/publicacao/ down_anexo.wsp?tmp.arquivo=E15_241e-PING%20v1.0%2013%2007%202005.pdf>. Acesso em: 14 jul. 2005. COSTA, Cláudio Giulliano Alves da. Desenvolvimento e Avaliação Tecnológica de um Sistema de Prontuário Eletrônico do Paciente, Baseado nos Paradigmas da World Wide Web e da Engenharia de Software. Campinas, 2001. Disponível em: <http://www.medsolution.com.br/claudio/dissertacao/Dissertacao_Claudio_Giulliano_PEP.pdf> . Acesso em: 07 jun. 2005.
COSTA, Marcos André da Silva. Um Modelo de Middleware Adaptativo. Recife,2004. Disponível em: <http://www.cin.ufpe.br/~nsr/DIS_MASC.doc>.Acesso em: 02 out. 2005.
FERNANDES, Rodrigo A. B., et al. O que é Health Level 7 (HL7)?. São Paulo,1999. Disponível em: <http://www.virtual.epm.br/material/tis/curr-med/temas/med5/med5t21999/estrutura/ hl7.htm>. Acesso em: 18 dez. 2004. FRICK, Oscar Osvaldo; LORO, Letícia. O Setor de Software para Saúde no Brasil. Economia e Tecnologia. Maio/Julho 2004. Disponível em:<http://www.iees.org.br/Download /revista.PDF>. Acesso em: 15 maio 2005. GESSER, Carlos Eduardo. GALS Gerador de Analisadores Léxicos e Sintáticos. 2002. Disponível em: <http://gals.sourceforge.net/help.html#Intro>. Acesso em: 25 set. 2005. GUEDES, Luiz Affonso. Objetos Distribuídos - Programação Distribuída Orientado a Objetos. Rio Grande do Norte, 2004. Disponível em: <http://www.dca.ufrn.br/~affonso/ DCA2401/2004_1/aulas/objetos_distribuidos.pdf>.Acesso em: 04 out. 2005. GUEDES, Ulisses T.V. Redes e Comunicação de Dados – Redes Programação TCP/ IP. Rio de Janeiro, 2001. Disponível em: <http://www.aedb.br/faculdades/eng/ /disciplinas/ano5/downloads/redes_3_1.pdf>. Acesso: 30 set. 2005. GUIMARÃES, Vinicius. LISA - Uma ferramenta para geração automática de linguagem. Disponível em: < http://atlas.ucpel.tche.br/~barbosa/consico/consico2/artigos/a3.pdf > Acesso em: 30 set. 2005.
60
GULIATO, Denise; CARNIELLI, Walter; ZUFFO, João Antônio. Integração de Crescimento de Região e Detecção de Arestas através de Operadores de Agregação. Disponível em http://mirror.impa.br/sibgrapi97/anais/pdf/art11.pdf . Acesso em: 02 out. 2005. HENRIQUES, Jorge; CARVALHO, Paulo de. HL7 Health Level Seven. 2005. Disponível em: <https://www.dei.uc.pt/weboncampus/course/LEI/2004-2005/hl7V23 Aulas.pdf>. Acesso em: 11 ago. 2005. HL7. HL7 Receives ANSI Approval of Three Version 3 Specifications Including CDA, Release 2 . Disponível em:<http://www.hl7.org/Press/20050505.pdf>. Acesso em:29 maio 2005. MACÊDO, Raimundo, et al. Tratando a Previsibilidade em Sistemas de Tempo-Real Distribuídos: Especificação, Linguagens, Middleware e Mecanismos Básicos. Disponível em:< http://www.lasid.ufba.br/public/artigos/tempoReal2004.pdf>. Acesso em: 01 out. 2005. MACIEL, Fabiano. O que é C++. Iniciando em C++. Fevereiro de 2004. Disponível em: <http://www.dotnetraptors.com.br/start/artigos/artigos_cpp/1294.aspx>. Acesso em: 20 set. 2005. MARCH, Alan. HL7, o Modelo. 2002. Disponível em: <http://www.salutia.com/pdf/Boletin _Salutia_maio_junho_2002.pdf >. Acesso em: 15 jun. 2005. MAZZUCO Jr, José. Uma Abordagem Híbrida do Problema da Programação da Produção através dos Algoritmos Simulated Annealing e Genético. Florianópolis, 1999. Disponível em: <http://www.eps.ufsc.br/teses99/mazzucco/cap4.html>. Acesso em: 30 set. 2005. MELLO Fabrízio de Royes; LACERDA, Guilherme Silva de. Aspectos Formais Sobre Linguagens de Programação. Março de 2001. Disponível em: <http://www.urcamp.tche.br/ ccei/revista7.pdf>. Acesso em: 20 out. 2005. MONGELLI, Henrique. Programação Distribuída. 2003. Disponível em: <http://www. dct.ufms.br/~mongelli/disciplinas/graduacao/progdistr2003/aula1.pdf>.Acesso em: 28 set. 2005. RODRIGUES, Denise, et al. Avanço da Informática Médica. São Paulo, 2000. Disponível em: <http://www.virtual.epm.br/material/tis/curr-med/temas/med5/med5t32000/grupo5/ avanco.htm>. Acesso em: 22 abr. 2005. SALUTIA. Salutia Tecnologia e Saúde. São Paulo, 2005. Disponível em: <http://www.salutia.com/home/home.php?idioma=br&id0=financiadores&id1=clientes> Acesso em: 12 dez. 2005.
SAÚDE TOTAL. Informática Médica uma nova disciplina. Disponível em: <http://www.saudetotal.com/infosb/infmed.htm>. Acesso em: 12 dez. 2005.
61
SILVA, Murilo F. , et al. Tipos de Sistemas de Informação em Saúde no Ambiente Hospitalar. São Paulo, 1999. Disponível em: <http://www.virtual.epm.br/material/tis/curr-med/temas/med5/med5t41999/sis/sub5.htm.> Acesso em: 30 abr. 2005. SIQUEIRA, Frank Augusto. Sistemas Distribuídos. Disponível em : <http://www.inf.ufsc.br/ ~frank/unileste/Sistemas%20Distribuidos%20-%20Parte%20II.pdf >. Acesso em: 30 set. 2005. VASCONCELLOS Neto, José Augusto. Tendências da Informática em Saúde no Brasil. Economia e Tecnologia. Maio/Julho 2004. Disponível em: <http://www.iees.org.br/ Download/revista.PDF>. Acesso em: 15 jun. 2005. WIKIPEDIA. Cliente-servidor. Disponível em: <http://pt.wikipedia.org/wiki/Cliente-servidor> Acesso em: 01 out. 2005.
62
ANEXO 1 – ANÁLISE DE REQUISITOS Caso de Uso Estendido - Modelo Especializado Caso de Uso: Envio de mensagens Ator: usuário da aplicação, aplicação. Finalidade: envio de mensagens para as aplicações interessadas. Tipo: primário e essencial. <pré-condição> mensagem deve estar semanticamente correta. <pós-condição> mensagem é enviada ao servidor. Visão Geral: usuário ou aplicação envia mensagem no formato HL7 ao servidor para ser encaminhado posteriormente ao seu(s) destinatário(s).
Seqüência Típica de Eventos Ação do Ator Resposta do Sistema 1) Este caso de uso inicia a partir do momento em que o usuário informa à aplicação uma admissão de paciente ou ainda quando o sistema deseja enviar uma confirmação ao remetente referente a mensagem recebida.
2) Sistema busca endereço IP do servidor. 3) Solicita conexão com o servidor. 4) Sistema envia mensagem (HL7) ao
servidor 5) Fecha conexão com o servidor Seqüência Alternativa *3.a. Caso não seja possível estabelecer conexão com o servidor, sistema alerta ao usuário a indisponibilidade do mesmo. Caso de Uso: Receber mensagem Ator: Sistema Servidor, Sistema Cliente. Finalidade: receber mensagens. Tipo: primário e essencial. <pré-condição> mensagem enviada deve estar semanticamente correta. Visão Geral: sistema aguarda por novas solicitações de conexão para recebimento de mensagem.
63
Seqüência Típica de Eventos Ação do Ator Resposta do Sistema 1) Este caso de uso começa quando servidor ou aplicação cliente solicita conexão para o envio de mensagem.
2) Aceita nova conexão 3) Recebe mensagem no formato HL7 4) Faz verificação semântica da mensagem
recebida. 5) Armazena dados recebidos em sua base
de dados. 6) Encaminhar mensagem recebida ao
destinatário. Seqüência Alternativa *4.a. Se a mensagem estiver semanticamente incorreta, sistema encerra execução. *4.b Caso o ator seja o servidor, o passo 5 deverá ser ignorado e seguir seqüência de execução a partir do passo 6. *4.c Caso o ator seja o aplicação cliente, a execução deverá ser encerrada no passo 5.
*4.c.a Se tipo da mensagem recebida for de admissão de paciente, a aplicação encaminha ao servidor uma mensagem de confirmação.
*4.c.b Se tipo de mensagem recebida for de confirmação, o passo 5 não será executado. Caso de Uso: Encaminhar mensagem ao destinatário Ator: Servidor Finalidade: encaminhar mensagens recebidas aos seus respectivos destinatários. Tipo: primário e essencial. <pré-condição> destino deverá estar registrado na base. <pós-condição> mensagem enviada ao destino. Visão Geral: servidor analisa mensagem e localiza destino em sua base para então encaminhar mensagem ao destino.
Seqüência Típica de Eventos Ação do Ator Resposta do Sistema 1) Este caso de uso inicia a partir do momento em que uma nova mensagem é recebida pelo servidor.
2) Verifica a existência do destino 3) Estabelece conexão com destinatário 4) Envia mensagem HL7 para destino 5) Fecha conexão com o cliente Seqüência Alternativa *2.a. Caso destino da mensagem não esteja registrado no servidor, envia mensagem de erro para remetente.
64
*3.a. Caso não seja possível estabelecer conexão com o cliente destino, mensagem deverá ser armazenada em sua caixa de saída. Caso de Uso: Verifica Caixa de Saída Ator: Sistema Servidor Finalidade: enviar mensagem que se encontra na caixa de saída e excluir da caixa de saída as mensagens expiradas. Tipo: primário <pré-condição> dados recuperados corretamente da base de dados. <pós-condição> mensagens encaminhadas deverão ser retiradas da caixa de saída. Visão Geral: de tempo em tempo servidor tenta enviar mensagens que se encontram na caixa de saída e verificar se não há mensagens expiradas.
Seqüência Típica de Eventos Ação do Ator Resposta do Sistema 1) Este caso de uso começa quando servidor verifica que há mensagens armazenadas em sua caixa de saída.
2) Verifica se mensagem HL7 da caixa de saída não se encontra expirada.
3) Estabelece conexão com o destino. 4) Mensagem da caixa de saída é enviada 5) Encerra conexão com o destino 6) Exclui mensagem da caixa de saída. Seqüência Alternativa *2.a. Caso destino da mensagem não seja mais válido, servidor retorna mensagem de aviso para o emissor da mensagem e executa o passo 6. *2.b. Se a mensagem estiver expirada (estiver 4 horas ou mais presente na caixa de saída), a mensagem será armazenada na base de dados como mensagem de erro e executa-se o passo 6. *3.a.Caso não seja possível estabelecer conexão com o destinatário, os passos seguintes não serão executados. Casos de Uso Estendido - Modelo Genérico Casos de Uso: Envio de mensagens do Cliente Ator: aplicação cliente. Finalidade: envio de mensagens para as aplicações interessadas. Tipo: primário . <pré-condição> mensagem deve estar semanticamente correta. <pós-condição> mensagem é enviada. Visão Geral: usuário ou aplicação envia mensagem no formato HL7 ao servidor para ser encaminhado posteriormente ao seu(s) destinatário(s).
65
Seqüência Típica de Eventos Ação do Ator Resposta do Sistema 1) Este caso de uso inicia a partir do momento em que a aplicação cliente envia mensagem no formato pré-estabelecido para o cliente.
2) Sistema busca endereço IP do servidor. 3) Solicita conexão com o servidor. 4) Sistema envia mensagem (HL7) ao
servidor 5) Fecha conexão com o servidor Seqüência Alternativa *3.a. Caso não seja possível estabelecer conexão com o servidor, sistema alerta ao usuário a indisponibilidade do mesmo. Caso de Uso: Receber mensagem Ator: Servidor, Cliente. Finalidade: receber mensagens. Tipo: primário e essencial. <pré-condição> mensagem enviada deve estar semanticamente correta. Visão Geral: sistema aguarda por novas solicitações de conexão para recebimento de mensagem.
Seqüência Típica de Eventos Ação do Ator Resposta do Sistema 1) Este caso de uso começa quando servidor ou aplicação cliente solicita conexão para o envio de mensagem.
2) Aceita nova conexão 3) Recebe mensagem 4) Faz verificação semântica da mensagem
recebida. 5) Encaminhar mensagem recebida ao
destinatário. Seqüência Alternativa *4.a. Se a mensagem estiver semanticamente incorreta, sistema encerra execução. Caso de Uso: Encaminhar mensagem do Cliente Ator: Servidor Finalidade: encaminhar mensagens recebidas aos seus respectivos destinatários. Tipo: primário e essencial. <pré-condição> destino deverá estar registrado na base de dados. <pós-condição> mensagem enviada ao destino.
66
Visão Geral: servidor analisa mensagem e localiza destino em sua base para então encaminhar mensagem ao destino.
Seqüência Típica de Eventos Ação do Ator Resposta do Sistema 1) Este caso de uso inicia a partir do momento em que uma nova mensagem é recebida pelo servidor.
2) Verifica a existência do destino 3) Adiciona na mensagem HL7 a porta
referente à aplicação destino. 4) Estabelece conexão com destinatário 5) Envia mensagem para destino 6) Fecha conexão com o cliente Seqüência Alternativa *2.a. Caso destino da mensagem não esteja registrado no servidor, envia mensagem de erro para remetente. *4.a. Caso não seja possível estabelecer conexão com o cliente destino, mensagem deverá ser armazenada em sua caixa de saída Caso de Uso: Encaminhar mensagem do Servidor Ator: Cliente Finalidade: encaminhar mensagens recebidas do servidor as suas respectivas aplicações (destinatários). Tipo: primário e essencial. <pós-condição> mensagem enviada ao destino. Visão Geral: servidor analisa mensagem e verifica em qual porta a aplicação destino deverá estar aguardando para então encaminhar a mensagem à aplicação em um formato pré-estabelecido diferente de HL7.
Seqüência Típica de Eventos
Ação do Ator Resposta do Sistema 1) Este caso de uso inicia a partir do momento em que uma nova mensagem é recebida pelo cliente.
2) Verifica na mensagem a porta em que a mensagem deverá ser encaminhada
3) extrai da mensagem as informações úteis para a aplicação destino.
4) Gerar mensagem em um formato diferente do padrão HL7
5) Estabelece conexão com a aplicação destinatário
6) Envia mensagem para aplicação 7) Encerra conexão com a aplicação
67
Seqüência Alternativa *5.a Dependendo do tipo de mensagem será necessário o envio de mensagem ao remetente da mensagem confirmando o recebimento. Caso de Uso: Receber mensagem Ator: Cliente Finalidade: receber mensagens vindas do Cliente. Tipo: primário e essencial. <pré-condição> mensagem enviada deve ter sido construída corretamente de acordo com o formato pré-estabelecido. Visão Geral: aplicação aguarda por novas solicitações de conexão para recebimento de mensagem.
Seqüência Típica de Eventos Ação do Ator Resposta do Sistema 1) Este caso de uso começa quando cliente solicita conexão para o envio de mensagem.
2) Aceita nova conexão 3) Recebe mensagem 4)Verifica tipo da mensagem 5) Armazena dados recebidos em sua base
de dados. Seqüência Alternativa *4.a Se tipo de mensagem recebida for diferente de inserção, o passo 5 não será executado *4.a.b Caso tipo de mensagem seja de busca, aplicação deverá encaminhar ao cliente uma mensagem contendo resultado da busca. .Caso de Uso: Verificar Caixa de Saída Ator: Sistema Servidor Finalidade: enviar mensagem que se encontra na caixa de saída e excluir da caixa de saída as mensagens expiradas. Tipo: primário <pré-condição> dados recuperados corretamente da base de dados. <pós-condição> mensagens encaminhadas deverão ser retiradas da caixa de saída. Visão Geral: de tempo em tempo servidor tenta enviar mensagens que se encontram na caixa de saída e verificar se não há mensagens expiradas.
68
Seqüência Típica de Eventos Ação do Ator Resposta do Sistema 1) Este caso de uso começa quando servidor verifica que há mensagens armazenadas em sua caixa de saída.
2) Verifica se mensagem da caixa de saída não se encontra expirada.
3) Estabelece conexão com o destino. 4) Mensagem da caixa de saída é enviada 5) Encerra conexão com o destino 6) Exclui mensagem da caixa de saída. Seqüência Alternativa *2.a. Caso destino da mensagem não seja mais válido, servidor retorna mensagem de aviso para o emissor da mensagem e executa o passo 6. *2.b. Se a mensagem estiver expirada (estiver 4 horas ou mais presente na caixa de saída), a mensagem será armazenada na base de dados como mensagem de erro e executa-se o passo 6. *3.a.Caso não seja possível estabelecer conexão com o destinatário, os passos seguintes não serão executados. Requisitos Apresentamos os requisitos básicos dos modelos que foram elaborados inicialmente. Requisitos Funcionais
R1 – Os modelos deverão ser capazes de encaminhar as mensagens somente às aplicações interessadas.
R2 – Os modelos deverão permitir o envio de mensagens. R3 – Os modelos deverão permitir a recepção de mensagens. R4 – Deverão ser capazes de construir mensagens semanticamente corretas.
R5 – Deverá ser possível o reenvio de mensagens que não foram encaminhadas aos destinos com sucesso.
R6 – Deve ser informado ao sistema emissor a recepção de mensagens efetuadas com sucesso através de mensagens de confirmação. R7 – Permitir que Cliente dê suporte a todas as aplicações registradas em uma máquina.
69
Requisitos Não Funcionais RN1 – Tempo de resposta: é necessário que o sistema tenha um tempo de resposta minimizado para que haja consistência dos dados. RN2 – Independência do Sistema Operacional: os modelos deverão ser independentes do sistema operacional, sendo executados tanto em ambiente Linux como em ambiente Windows. RN3 – Independência da Linguagem: os modelos devem estabelecer comunicação entre as aplicações, independente da linguagem. Requisitos para Utilização do Sistema A utilização do middleware HL7, exige o cumprimento de determinadas regras e instalação e/ou configuração certas ferramentas. Requisitos de Software para Usuário Final Para o correto funcionamento do sistema, é necessário que esteja disponível a DLL denominada PThreadGC tanto no cliente HL7 como no servidor HL7. Para a utilização do servidor é necessário o uso do banco de dados PostgreSQL e criação das respectivas tabelas. Cliente HL7 pThreadGC.dll Servidor HL7 pThreadGC.dll PostgreSQL Base de dados: HL7 Tabela: caixasaida Campos: "ipDestino" varchar(20) NOT NULL, "ipOrigem" varchar(20) NOT NULL, porta int2 NOT NULL, mensagem text, "idMsg" varchar(25) NOT NULL, "dataHora" timestamp Chave primária: ("ipDestino", porta, "idMsg") Tabela: configuracoes Campos :
ip varchar(20) NOT NULL, porta varchar(20) NOT NULL, aplicacao varchar(30) NOT NULL Chave primária: (ip, porta, aplicacao) Tabela: erros Campos: "tipoErro" text,
data varchar(30)
70
O arquivo ConfigServidorHL7.txt é utilizado pelo servidor HL7 para armazenar as configurações da base de dados HL7, assim, deverá ser informado o IP do servidor, porta, nome banco, usuário e senha, onde os campos deverão estar separados por “#” como no exemplo a seguir:
localhost#5432#HL7#postgres#12345 Todos os clientes HL7 deverão preencher no arquivo ConfigServidor.txt, o endereço IP do servidor HL7. Requisitos de Desenvolvimento Para que o middleware possa ser utilizado, os seguintes requisitos devem ser cumpridos: Servidor HL7
• Disponibilizar a porta 27015 • IP fixo • Sincronização do relógio do sistema • Cadastro das aplicações (tabela configuracoes)
Cliente HL7
• Nome Aplicação • IP fixo • Conhecer IP do servidor (arquivo de configuração) • Sincronização do relógio do sistema • Disponibilizar a porta 27014
Aplicação
• Utilização de Sockets • Possibilidade uso de threads • Extrair token
A seguir é mostrada a estrutura das mensagens recebidas e/ou enviadas. Note que, os campos são separados por “|” e quando se tratar de um recebimento de mensagem, após o caractere “¨” estará a mensagem HL7 que foi enviada.
Cadastro de Paciente (Interação cliente-aplicação) 1. Enviar Mensagens
o Mensagem de Admissão: � Nome da Aplicação= “CadastroPaciente” + ”|” � Tipo da mensagem= “CadastroPaciente-Inclusao” + “|” � Informar dados demográficos do paciente(ver campos do PID) + “|”
o Mensagem de Resposta Busca: � Nome da Aplicação = “CadastroPaciente” + ”|” � Tipo da mensagem= CadastroPaciente-Resposta + ”|” � Aplicação Origem Localizar paciente através de ID:
71
• SE encontrado = “OK” + ”|” o Retornar os dados demográficos do paciente(PID) + ”|”
• SENÃO encontrado = “NF” + ”|”
o Retornar (PID) + ”|”
2. Receber Mensagens o ACK (mensagem de confirmação)
� Idmensagem +”|” � Tipo mensagem = RQP +”|” � Aplicação Origem + “|” � DataHoraMensagem + “|” � Mensagem: “¨”
o RQP (mensagem de busca) � Idmensagem + ”|” � Tipo mensagem = RQP + ”|” � Aplicação Origem + ”|” � DataHoraMensagem + “|” � ID paciente + ”|” � Tabela= DEM + ”|” � Mensagem: “¨”
Gerência Exames (Interação cliente-aplicação)
1. Enviar Mensagens o Nome da Aplicação = “GerenciaExames” + “|” o Informar Tipo da mensagem= “GerenciaExames-ResultadoExames”+”|”
� Informar ID do paciente +”|” � Tabela = “RES” + “|” � Total de resultados +”|” � Resultado de Exames Efetuados +”|”
o Informar Tipo da mensagem= “GerenciaExames-Busca” + “|” � Informar ID do paciente +”|” � Tabela = “DEM” + “|”
2. Receber Mensagens o ACK (mensagem de confirmação)
� Idmensagem +”|” � Tipo mensagem = RQP +”|” � Aplicação Origem + “|” � DataHoraMensagem + “|” � Mensagem: “¨”
o ADT (mensagem de admissão) � ID mensagem + ”|” � Tipo mensagem =ADT + ”|” � Aplicação Origem + ”|” � Data hora mensagem +”|” � PID + ”|” � Mensagem: “¨”
72
o QRI (mensagem de resposta busca)
� ID mensagem + ”|” � Tipo mensagem =QRI +”|” � Aplicação Origem + ”|” � Data hora mensagem +”|” � Encontrado = “OK” +”|” ou Encontrado = “NF” +”|” � PID +”|” � Mensagem: “¨”
Prontuário Eletrônico (Interação cliente-aplicação) Receber Mensagens
o ACK (mensagem de confirmação) � Idmensagem +”|” � Tipo mensagem = RQP +”|” � Aplicação Origem + “|” � DataHoraMensagem + “|” � Mensagem: “¨”
o ADT (mensagem de admissão) � ID mensagem + ”|” � Tipo mensagem =ADT + ”|” � Aplicação Origem +”|” � Data hora mensagem + ”|” � PID + ”|” � Mensagem: “¨”
o PIN (mensagem de atualização prontuário) � ID mensagem +”|” � Tipo mensagem =QRI + ”|” � Aplicação Origem + ”|” � Data hora mensagem + ”|” � idPaciente + “|” � Tabela + “|” � QuantExames +”|” � Exames + ”|” � Mensagem: “¨”
Campos do PID (Identificação de Paciente) Set_ID – identificador paciente Patient_ID_External - identificador paciente Patient_ID_Internal – identificador paciente (obrigatório) Alternate_Patient_ID – identificador paciente Patient_Name – nome completo Mother_Maiden_Name – nome de solteiro da mãe Date_Time_of_Birth – Data de nascimento (AnoMesDia HoraMinutos)
73
Sex – Sexo (F - feminino, M - masculino, O - outro, T - desconhecido) Patient_Alias - Race - Raça Patient_Address - Endereço Phone_Number_Home – Telefone Residencial Phone_Number_Business – Telefone Comercial Primary_Language - Idioma Marital_Status – Estado civil (A - separado, D-divorciado, M - casado, S- solteiro, W – viúvo) Religion – Religião Patient_Account_Number - Número da conta SSN_Number – Número de seguridade social Driver_License_Number – Carteira de Habilitação Mother_Identifier – Identificador Ethnic_Group – Grupo Étnico Birth_Place – Naturalidade Multiple_Birth_Indicator – Indicador de nascimento múltiplo (Y/N) Birth_Order – Ordem de nascimento múltiplo Citizenship - Cidadania Veterans_Military_Status – Status militar Nationality - Nacionalidade Patient_Death_Date_and_Time – Data e horário Falecimento(AnoMesDia HoraMinutos) Patient_Death_Indicator – Indicador de Falecimento
74
D
iagr
ama
de C
lass
es
75
Telas do Sistema Aplicação Cadastro Paciente
Nas figuras 30 e 31 são mostradas as telas da aplicação Cadastro Paciente.
Figura 30 – Cadastro de Paciente
Figura 31 – Recebimento de mensagem
76
Aplicação Prontuário Eletrônico Nas figuras 32,33 e 34 são mostradas as telas da aplicação prontuário
Figura 32 – Prontuário Eletrônico - Recebimento de Cadastro de Paciente
Figura 33 – Prontuário Eletrônico - Recebimento de resultado de exames
Figura 34 – Prontuário Eletrônico - Mensagem de recebimento de cadastro
77
Aplicação Gerência de Exames Nas figuras 35, 36, 37, 38 são mostradas as telas da aplicação Gerência de Exames.
Figura 35 – Gerência de Exames - Cadastro Exame
Figura 36 – Gerência Exames - Buscar Paciente
78
Figura 37 – Gerência Exames - Resultado Exame
Figura 38 – Gerência Exames - Mensagens Recebidas
79
Servidor HL7 A figura 39 mostra o recebimento de uma mensagem de admissão de paciente, onde o servidor irá encaminhar a mensagem para todos os clientes HL7 que possuem aplicações interessadas neste tipo de mensagem.
Figura 39 – Servidor HL7 - Recebimento de mensagem admissão de paciente
Cliente HL7 A figura 40 mostra o recebimento de uma mensagem no formato HL7 e o envio desta mesma mensagem para o formato que a aplicação conhece.
Figura 40 – Cliente HL7 - Recebimento de mensagem
80
ANEXO 2 – GRAMÁTICA IMPLEMENTADA <MSH> ::= MSH #1<Field_Separator>; <Field_Separator>::= "|"<Encoding_Caracters>; <Encoding_Caracters> ::= "^~\&"#3 "|"<Sending_application>; <Sending_application>::= <componente_Sending_application> "|"<Sending_Facility>; <componente_Sending_application>::= id#4<componente_Sending_application>| "^"#4<componente_Sending_application>|"&"#4<componente_Sending_application>|î; <Sending_Facility> ::= "|"<Receiving_application>|id#5 "|"<Receiving_application>; <Receiving_application> ::= id#6 "|"<Receiving_Facility>|"|"<Receiving_Facility>; <Receiving_Facility> ::= "|"<Date_Time_Message> |id#7 "|"<Date_Time_Message>; <Date_Time_Message> ::= id#8"|"<Security>|"|"<Security>; <Security> ::= "|"#9<Message_Type_Event>|id#9"|"<Message_Type_Event>; <Message_Type_Event>::= id #10 "^" <Message_Type_Segment>; <Message_Type_Segment>::= "|"<Message_Control_ID>| id #1010"|" <Message_Control_ID>; <Message_Control_ID>::= "|"|id#11"|"<Processing_ID>; <Processing_ID>::= id#12"|"<Version_ID>|"|"<Version_ID>; <Version_ID>::= id#13 Fim_Linha#14<pos_MSH>; <pos_MSH>::=<PID>|<EVN>|<MSA>|<QRD>|<EQL>|<OBX>|<ORC>|<AL1>|<QAK>|î; <PID> ::= PID#15"|" <Patient_ID_1>; <Patient_ID_1> ::="|"<Patient_ID_2>| id #16 "|"<Patient_ID_2>; <Patient_ID_2> ::= <componente_Patient_ID_2>"|"<Patient_ID_3>; <componente_Patient_ID_2>::= id#17<componente_Patient_ID_2>|"^"
#17<componente_Patient_ID_2>|"&"#17<componente_Patient_ID_2>|î;
81
<Patient_ID_3>::= <componente_Patient_ID_3>"|"<Alternate_ID_4>; <componente_Patient_ID_3>::= id#18<componente_Patient_ID_3>|"^"#18
<componente_Patient_ID_3>|"&"#18<componente_Patient_ID_3>|î; <Alternate_ID_4>::= "|"<Patient_name>|id #19"|"<Patient_name>; <Patient_name> ::= <componente_Patient_name>"|"<Mother_Maiden_Name>; <componente_Patient_name>::= id#20<componente_Patient_name>|"^"#20
<componente_Patient_name>|"&"#20<componente_Patient_name>|î; <Mother_Maiden_Name> ::="|"<Date_Time_of_Birth>|id #21 "|"<Date_Time_of_Birth>; <Date_Time_of_Birth> ::="|"<Sex>|id #22"|"<Sex>; <Sex> ::= "|"<Patient_Alias>|id#23"|"<Patient_Alias>; <Patient_Alias>::="|"<Race>|id #24"|"<Race>; <Race> ::= "|"<Patient_Address>|id #25 "|" <Patient_Address>; <Patient_Address>::=<componente_Patient_Address>"|"<County_Code>; <componente_Patient_Address>::= id#26<componente_Patient_Address>|"^"#26
<componente_Patient_Address>|"&"#26<componente_Patient_Address>|î; <County_Code>::="|"#27<Phone_Number_Home>; <Phone_Number_Home>::=<componente_Phone_Number_Home>"|"<Phone_Number_Business>; <componente_Phone_Number_Home>::= id#28<componente_Phone_Number_Home>
|"^"#28<componente_Phone_Number_Home>|"&"#28 <componente_Phone_Number_Home>|î;
<Phone_Number_Business>::=<componente_Phone_Number_Business>"|"
<Primary_Language>; <componente_Phone_Number_Business>::= id#29<componente_Phone_Number_Business> |"^"#29<componente_Phone_Number_Business>|"&"#29
<componente_Phone_Number_Business>|î; <Primary_Language>::="|"<Marital_Status>|id #30"|"<Marital_Status>; <Marital_Status>::="|"<Religion>|id#31"|"<Religion>; <Religion>::= "|"<Patient_Account_Number>|id#32"|"<Patient_Account_Number>; <Patient_Account_Number>::=<componente_Patient_Account_Number>
82
<fim_Patient_Account_Number>; <componente_Patient_Account_Number>::= id#33 <componente_Patient_Account_Number>|"^"#33 <componente_Patient_Account_Number>|"&"#33 <componente_Patient_Account_Number>|î;
<fim_Patient_Account_Number>::= "|" <Social_Security_Number>|<fim_PID>; <Social_Security_Number>::= id#34 <fim_Social_Security_Number> |<fim_Social_Security_Number>; <fim_Social_Security_Number>::= "|"<Driver_License_Number>|<fim_PID>; <Driver_License_Number>::=id#35"^"#35id#35"^"#35id#35 <fim_Driver_License_Number>|<fim_Driver_License_Number>; <fim_Driver_License_Number>::= <fim_PID>|"|"<Mother_Identifier>; <Mother_Identifier>::=<componente_Mother_Identifier><fim_Mother_Identifier>; <componente_Mother_Identifier>::= id#36<componente_Mother_Identifier> |"^"#36<componente_Mother_Identifier>|"&"#36<componente_Mother_Identifier>|î; <fim_Mother_Identifier>::= <fim_PID>|"|"<Ethnic_Group>; <Ethnic_Group>::=<fim_Ethinic_Group>|id#37 <fim_Ethinic_Group>; <fim_Ethinic_Group>::= <fim_PID>|"|"<Birth_Place>; <Birth_Place>::= <fim_Birth_Place>|id#38 <fim_Birth_Place>; <fim_Birth_Place>::= <fim_PID>|"|"<Multiple_Birth_Indicator>; <Multiple_Birth_Indicator>::= <fim_Multiple_Birth_Indicator>|id#39 <fim_Multiple_Birth_Indicator>; <fim_Multiple_Birth_Indicator>::= <fim_PID>|"|"<Birth_Order>; <Birth_Order>::= <fim_Birth_Order>|id#40 <fim_Birth_Order>; <fim_Birth_Order>::= <fim_PID>|"|"<Citizenship>; <Citizenship>::=<fim_Citizenship>|id#41<fim_Citizenship>; <fim_Citizenship>::= <fim_PID>|"|"<Veterans_Military_Status>; <Veterans_Military_Status>::=<fim_Veterans_Military_Status>|id#42 <fim_Veterans_Military_Status>; <fim_Veterans_Military_Status>::= <fim_PID>|"|"<Nationality>; <Nationality>::=<fim_Nationality>|id#43 <fim_Nationality>; <fim_Nationality>::= <fim_PID>|"|"<Patient_Death_Date_Time>; <Patient_Death_Date_Time>::=<fim_Patient_Death_Date_Time>|id#44 <fim_Patient_Death_Date_Time>; <fim_Patient_Death_Date_Time>::= <fim_PID>|"|"<Patient_Death_Indicator>;
83
<Patient_Death_Indicator>::=<fim_PID>|id#45 <fim_PID>; <fim_PID>::= Fim_Linha#46 <pos_PID>; <pos_PID>::=<ORC>|<OBR>|<PRB>|<PV1>|<AL1>|î; <EVN>::= EVN #47 "|" <Event_Type_Code>; <Event_Type_Code>::= id #48"|"<Recorded_Date_Time>; <Recorded_Date_Time>::= id#49 <fim_Recorded_Date_Time>| <fim_Recorded_Date_Time>; <fim_Recorded_Date_Time>::= <fim_EVN>|"|"<Date_Time_Planned_Event>; <Date_Time_Planned_Event>::=id#50 <fim_Date_Time_Planned_Event>|<fim_Date_Time_Planned_Event>; <fim_Date_Time_Planned_Event>::=<fim_EVN>|"|" <Event_Reason_Code>; <Event_Reason_Code>::=id #51<fim_Event_Reason_Code>| <fim_Event_Reason_Code>; <fim_Event_Reason_Code>::=<fim_EVN>|"|"<Operator_ID>; <Operator_ID>::= id #52<fim_Operator_ID>| <fim_Operator_ID>; <fim_Operator_ID>::=<fim_EVN>|"|"<Event_Occurred>; <Event_Occurred>::=id#53 <fim_EVN> |<fim_EVN>; <fim_EVN>::= Fim_Linha#54 <pos_evento>; <pos_evento>::= î|<PID>|<MSA>; <MSA>::= MSA#55 "|" <Acknowledgment_Code>; <Acknowledgment_Code>::=id#56"|" <Message_Control_ID_MSA>; <Message_Control_ID_MSA>::=id#57 <fim_Message_Control_ID_MSA>| <fim_Message_Control_ID_MSA>; <fim_Message_Control_ID_MSA>::= <fim_MSA>|"|"<Text_Message>; <Text_Message>::= id#58 "|"<Expected_Sequence_Number>|"|" <Expected_Sequence_Number>|<fim_MSA>; <Expected_Sequence_Number>::= id #59"|"<Delayed_Acknowledgment_Type> | "|"<Delayed_Acknowledgment_Type>|<fim_MSA>;
84
<Delayed_Acknowledgment_Type>::= id #60"|"<Error_Condition>|"|"<Error_Condition>|<fim_MSA>; <Error_Condition>::= id#61 <fim_MSA>|<fim_MSA>; <fim_MSA>::= Fim_Linha#62 <pos_MSA>; <pos_MSA>::= <PID>|<EVN>|<QAK>|<ORC>|<QRD>|î; <QRD>::= QRD #114"|"<Query_date_time>; <Query_date_time>::= id #115"|" <Query_format_code>; <Query_format_code>::= id#116 "|"<Query_priority>; <Query_priority>::= id#117 "|" <Query_ID>; <Query_ID>::= id#118 "|" <Deferred_response_type>; <Deferred_response_type>::= "|" <Deferred_response_date_time> | id#119 "|" <Deferred_response_date_time>; <Deferred_response_date_time>::= "|" <Quantity_limited_request>| id#120 "|"<Quantity_limited_request>; <Quantity_limited_request>::= id #121"|" <Who_subject_filter>; <Who_subject_filter>::=<componente_Who_subject_filter>"|"<What_subject_filter>; <componente_Who_subject_filter>::= id #122<componente_Who_subject_filter> |"^"#122<componente_Who_subject_filter>|"&"#122<componente_Who_subject_filter>|î; <What_subject_filter>::= <componente_What_subject_filter>"|" <What_department_data_code>; <componente_What_subject_filter>::= id #123<componente_What_subject_filter>| "^"#123<componente_What_subject_filter>|î; <What_department_data_code>::= <componente_What_department_data_code>"|" <What_data_code_value_qualifier>; <componente_What_department_data_code>::= id #124<componente_What_department_data_code>|"^"#124 <componente_What_department_data_code>|î; <What_data_code_value_qualifier>::= "|"<Query_results_level>| id #125"|" <Query_results_level>;
85
<Query_results_level>::= <fim_QRD>|id#126 <fim_QRD>; <fim_QRD>::= Fim_Linha#127 <pos_QRD>; <pos_QRD>::= <QRF>|<DSP>|î; <QRF>::= QRF#128 "|"<Where_Subject_Filter>; <Where_Subject_Filter>::= id #129<fim_Where_Subject_Filter> |<fim_Where_Subject_Filter>; <fim_Where_Subject_Filter>::= <fim_QRF>|"|"<When_Data_Start_Date_Time>; <When_Data_Start_Date_Time>::= id #130<fim_When_Data_Start_Date_Time> |<fim_When_Data_Start_Date_Time>; <fim_When_Data_Start_Date_Time>::= <fim_QRF>|"|"<When_Data_End_Date_Time>; <When_Data_End_Date_Time>::= id #131<fim_When_Data_End_Date_Time>| <fim_When_Data_End_Date_Time>; <fim_When_Data_End_Date_Time>::= <fim_QRF>|"|"<What_User_Qualifier>; <What_User_Qualifier>::= id #132<fim_What_User_Qualifier> |<fim_What_User_Qualifier>; <fim_What_User_Qualifier>::= <fim_QRF>|"|"<Other_QRY_Subject_Filter>; <Other_QRY_Subject_Filter>::= id#133 <fim_Other_QRY_Subject_Filter>| <fim_Other_QRY_Subject_Filter>; <fim_Other_QRY_Subject_Filter>::= <fim_QRF>|"|"<Which_Date_Time_Qualifier>; <Which_Date_Time_Qualifier>::=id#134 <fim_Which_Date_Time_Qualifier> |<fim_Which_Date_Time_Qualifier>; <fim_Which_Date_Time_Qualifier>::= <fim_QRF>|"|" <Which_Date_Time_Status_Qualifier>; <Which_Date_Time_Status_Qualifier>::=id#135 <fim_Which_Date_Time_Status_Qualifier>|<fim_Which_Date_Time_Status_Qualifier>; <fim_Which_Date_Time_Status_Qualifier>::= <fim_QRF>|"|" <Date_Time_Selection_Qualifier>; <Date_Time_Selection_Qualifier>::= id#136 <fim_Date_Time_Selection_Qualifier> |<fim_Date_Time_Selection_Qualifier>; <fim_Date_Time_Selection_Qualifier>::= <fim_QRF>|"|" <When_Quantity_Timing_Qualifier>; <When_Quantity_Timing_Qualifier>::= <componente_When_Quantity_Timing_Qualifier><fim_QRF>;
86
<componente_When_Quantity_Timing_Qualifier>::= id #137 <componente_When_Quantity_Timing_Qualifier>| "^"#137<componente_When_Quantity_Timing_Qualifier>|î; <fim_QRF>::= Fim_Linha #138<pos_QRF>; <pos_QRF>::= î; <DSP>::= DSP#160"|"<Display_Level>; <Display_Level>::= id#161 "|" <Data_Line>| "|"<Data_Line>; <Data_Line>::= id #162<fim_Data_Line> | <fim_Data_Line>; <fim_Data_Line>::= "|"<Logical_Break_Point>|<fim_DSP>; <Logical_Break_Point>::= id#163 <fim_Logical_Break_Point>| <fim_Logical_Break_Point>; <fim_Logical_Break_Point>::= "|"<Result_ID>|<fim_DSP>; <Result_ID>::= id#164 <fim_DSP>; <fim_DSP>::= Fim_Linha #165<pos_DSP>; <pos_DSP>::= <DSP>| î; <QAK>::= QAK #173"|" <Query_Tag_QAK>; <Query_Tag_QAK>::= id#174 "|" <Query_Response_Status> |"|" <Query_Response_Status>; <Query_Response_Status>::= id#175 Fim_Linha#176 <pos_QAK>| Fim_Linha#176<pos_QAK>; <pos_QAK>::= <DSP>|<QRD>|<EVN>|<RDF>|<PID>|î;
87
ANEXO 3 – ARTIGO
Modelo de Interoperabilidade de Sistemas Hospitalares Utilizando o Padrão HL7
KARINE PETRY
PAULA MARIEN ALBRECHT LOPES
Universidade Federal de Santa Catarina Departamento de Informática e Estatística
[email protected] [email protected]
______________________________________________________________ Resumo Este artigo descreve um modelo de interoperabilidade de sistemas de saúde através do desenvolvimento de um middleware projetado segundo as especificações do padrão HL7. Palavras-chave: interoperabilidade, saúde, HL7, middleware. _________________________________________________________________________
Introdução Em virtude da disparidade existente entre os sistemas projetados para o mundo dos negócios e aqueles que englobam a área da saúde, tornou-se necessário à elaboração de mecanismos que facilitem as transações num meio constituído por diversas peculiaridades como é o serviço de saúde em qualquer lugar do globo.
O ambiente de saúde merece uma atenção especial, pois os registros de informações de saúde de um paciente como laudos, sinais vitais, exames laboratoriais, e imagens médicas, não devem ser armazenados exclusivamente em papel, e sim, em meio eletrônico, para então proporcionar uma disponibilidade agilizada, de modo confiável, com integridade garantida, admitindo redução de custos e ainda com uma independência física do local de armazenamento. Dessa forma, surgiu o Prontuário Eletrônico de Pacientes (PEP), com a finalidade de
permitir a integração e armazenamento da informação clínica e administrativa de pacientes de forma individual.
O emprego do prontuário eletrônico trouxe como conseqüência imediata à necessidade de uma padronização para garantir a transmissão das informações clínicas de um paciente, entre os mais diversos sistemas de informação que englobam um ambiente de saúde. Assim, com este propósito, foram projetados alguns padrões, destacando-se o padrão HL7 a nível internacional. Visão Geral do HL7 Em 1987, foi fundada uma organização sem fins lucrativos denominada HL7( Health Level Seven), [2] com a responsabilidade de produzir normas para a área de saúde, relacionados com a informação clínica e administrativa.
O padrão especifica quais as mensagens a serem trocadas entre os
88
diversos setores do hospital. Dentre os vários tipos de mensagens definidos pelo HL7 podem ser citadas: gestão de pacientes ao nível de admissão, transferências e saídas, pedidos, resultados, observações clínicas e contabilidade.
A unidade básica de informação, a ser trocada entre os vários intervenientes, é designada no padrão HL7 por mensagem. O padrão especifica além das características principais da troca de mensagens entre sistemas distintos, os vários tipos de mensagem e respectiva constituição. Genericamente uma mensagem é constituída por segmentos, que por sua vez são constituídos por campos, sendo estes últimos organizados em componentes.
Assim, o padrão HL7 define o formato das mensagens, ou seja, descreve como os dados devem ser representados, o seu tipo e quais os caracteres usados para delimitar os vários segmentos numa mensagem. Também especifica as circunstâncias em que as mensagens devem ocorrer, regras como conseqüência de eventos (trigger event). Por exemplo, se for definido que os dados pessoais de um paciente devem ser disponibilizados, então quando houver uma admissão de paciente, automaticamente deve ser desencadeada uma mensagem que transmita a informação a todos os interessados (consulta não solicitada – unsolicited message).
O padrão HL7 também define os procedimentos que devem ser realizados na ocorrência de certos erros entre as aplicações. Por exemplo, caso uma aplicação não receba os dados que esperava, esta pode comunicar o fato para a aplicação emissora através de uma mensagem de erro. Por outro lado, se a transmissão dos dados foi efetuada
corretamente, então uma mensagem de confirmação (ACK-acknowledge) pode igualmente ser enviada. Garantindo, portanto confiabilidade na troca de mensagens.
Podemos considerar os principais tipos básicos de mensagens:
• Gestão de pacientes ou ADT - relativas à gestão da estada do paciente no hospital, admissão, transferência e saídas de pacientes por esta razão também conhecidas por mensagens ADT - admissions, discharges, transfers. São desencadeadas sem uma consulta prévia explícita (unsolicited messages).
• Pedidos/Querys – utilizadas para requisição (consulta) de informação. Uma situação típica ocorre quando um dos setores (farmácia, laboratório) consulta o sistema de admissão com respeito à informação de um paciente;
• Resultados/Observações – Resulta como resposta a uma mensagem do tipo (pedido/query) e a sua função é permitir a transmissão de observações clínicas, por exemplo;
• Outras - relativas à informação contabilística e financeira, experiências clínicas, bases de dados, etc.[1]
Na figura 1, é mostrado um
exemplo de mensagem de admissão de paciente (ADT^A01) composta pelos segmentos MSH, EVN, PID.
89
Figura 1 – Mensagem de admissão de paciente O HL7 não restringe o sistema
operacional, nem a linguagem de programação a ser utilizada para a troca de informação. Além disso, o HL7 é independente de meio físico e de protocolo de comunicação. O padrão HL7 permite ao implementador definir suas próprias mensagens, o que mais uma vez, torna-o flexível.
Modelo Desenvolvido Considerando que a implementação do padrão HL7 é uma tarefa árdua (mencionada em bibliografias pesquisadas), e realmente constatada na prática, cogitou-se então, o desenvolvimento de um middleware para servir como uma camada de apoio ao desenvolvedor de sistemas legados. Assim, com este objetivo, solucionou-se a integração de aplicações sem a necessidade de estas implementarem o padrão HL7, sendo apenas necessário que as mesmas sejam capazes de enviar streams seguindo uma estrutura mais simples que a proposta pelo HL7. Para isto, foi desenvolvido um modelo de sistema que apresentasse comportamento semelhante de um middleware, para disponibilizar os serviços oferecidos pelo HL7 utilizando um servidor, clientes e aplicações permitindo dessa forma a interoperabilidade de sistemas hospitalares.
Através da figura 2 é possível compreender a estrutura deste modelo.
Figura 2 – Mensagem de admissão de paciente
As aplicações GE, CP e PEP representadas na figura 2, tratam de aplicações referentes respectivamente a gerência de exames laboratoriais, cadastro de pacientes e prontuário eletrônico de pacientes. Vale lembrar que tais aplicações foram desenvolvidas apenas ao nível de teste possibilitando a análise da execução do modelo construído.
A troca de mensagens entre as aplicações e o sistema cliente utiliza uma estrutura simples, diferente do HL7. As informações enviadas pela aplicação ao seu respectivo cliente deverão estar separadas por um símbolo pré-definido, em nosso exemplo, utilizamos o símbolo “|” como separador. Já a troca de mensagens entre os sistemas cliente e servidor devem ser geradas seguindo a estrutura proposta pelo protocolo de comunicação HL7. Assim, caso a aplicação CP deseje enviar mensagem de admissão para as aplicações GE e PEP, ela deverá enviar as informações demográficas do novo paciente, bem como a aplicação destino e o tipo de mensagem. A informação referente à aplicação destino é válida apenas nos casos em que o tipo de mensagem a ser enviada é uma confirmação, solicitação de busca ou resultado de busca, nos casos em que o tipo de mensagem é referente a uma admissão, tal informação deverá ser preenchida apenas de forma representativa,
MSH|^~\&|ADT1|CadPaciente|GerenciaExames|CadPaciente |20051003091411||ADT^A01| IDMSH20051003 09:14:11|P|2.3<cr> EVN|A01|20051003 09:14:11<cr> PID| | |TR54| |Silva^Marcelo^Loyola^II^Dr |Maria Ferreira|194510100000|M||Asiatica|Rua Minas Gerais 55||(48)9833-8956|(48)398-6665|Portugues|Solteiro|Adventista |989896632|96556 |98989^055^65||Desconhecido|Treze Tilias|Sim|3|Brasileiro||Brasileiro|1900121200 | <cr>
90
uma vez que este tipo de mensagem é encaminhada pelo servidor a todos os aplicativos registrados em sua base de dados. A partir do momento que o cliente receber a mensagem da aplicação, esta deverá ser reconstruída seguindo as normas de especificação do padrão HL7. Nas figuras 3 e 4 podemos verificar a mensagem enviada pela aplicação e a mesma mensagem sendo então encaminhada pelo cliente ao servidor.
Figura 3 - Mensagens Trocadas entre Aplicação e Cliente
Figura 4 - Mensagens Trocadas entre Cliente e Servidor
Conclusão Como a tarefa de implementação do padrão HL7 se mostrou demasiadamente exaustiva, percebeu-se a necessidade do desenvolvimento de um middleware para facilitar o trabalho do desenvolvedor, que então poderia integrar seus sistemas, sem tomar conhecimento da complexidade que envolve a implementação das especificações do padrão HL7. Constatou-se que o esforço necessário para o desenvolvimento deste middleware foi compensador, visto que objetivo principal do padrão HL7 - a interoperabilidade de sistemas hospitalares - é realmente alcançado. Referências
[1] HENRIQUES, J.; CARVALHO, P. HL7 Health Level Seven. https://www.dei.uc.pt/weboncampus/course/LEI/2004-2005/hl7V23Aulas.pdf. (11/08/2005) [2] HL7 USA Central Web Pages. http://www.hl7.org. (18/08/2005).
91
ANEXO 4 – CÓDIGO FONTE ----------------------Classe MainInterface.cpp (Aplicação Gerência de Exames)----------------- #include "MainInterface.h" #include <glib.h> #include <gtk/gtksignal.h> #include <gtk/gtk.h> #include <pthread.h> #include <stdio.h> #ifndef unix #include <io.h> #endif #include <iostream> #include <fstream> #include <exception> #include <iomanip> using namespace std; PGconn *conn; //Cria um ponteiro para uma estrutura GladeXML GladeXML * xmlJP,*xmlJRE,*xmlJCE; GtkWidget* w, *entry; GtkWidget *janelaConsultaExame, *janelaResultadoExame; gchar *id_etiqueta_ResulExame; char *tem_cad_paciente; //corpo principal do Programa int main(int argc,char *argv[]) { g_thread_init (NULL); gdk_threads_init (); gdk_threads_enter ();/*iniciaiza a thread para q o sistema possa ouvir e rodar a interface ao mesmo tempo*/ gtk_init(&argc,&argv); //Inicializa o GTK glade_init();//Inicializa o Glade–opcional //Lê o arquivo com a estrutura XML da sua aplicação xmlJP = glade_xml_new("interface/interface.glade","JanelaPrincipal",NULL); //inicializar eventos(signals) da Janela Principal w = glade_xml_get_widget (xmlJP, "buttonBuscar"); gtk_signal_connect(GTK_OBJECT(w), "clicked", GTK_SIGNAL_FUNC(buscarExame), NULL); w = glade_xml_get_widget (xmlJP, "buttonNovo"); gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(novoExame),NULL);
92
w = glade_xml_get_widget (xmlJP, "buttonSair"); gtk_signal_connect(GTK_OBJECT(w),"clicked", GTK_SIGNAL_FUNC(sair), NULL); w = glade_xml_get_widget (xmlJP, "buttonSalvar"); gtk_signal_connect(GTK_OBJECT(w), "clicked", GTK_SIGNAL_FUNC(salvarExame), NULL); w = glade_xml_get_widget (xmlJP, "buttonSelecionaExame"); gtk_signal_connect(GTK_OBJECT(w),"clicked", GTK_SIGNAL_FUNC(buscarTipoExame), NULL); w = glade_xml_get_widget (xmlJP, "buttonOKBusca"); gtk_signal_connect(GTK_OBJECT(w),"clicked", GTK_SIGNAL_FUNC(buscarExamesCadastrados), NULL); w = glade_xml_get_widget (xmlJP, "buttonResultado"); gtk_signal_connect(GTK_OBJECT(w),"clicked", GTK_SIGNAL_FUNC(verResultadoExame), NULL); w = glade_xml_get_widget (xmlJP, "entryIDInterno"); gtk_signal_connect(GTK_OBJECT(w),"focus_out_event", GTK_SIGNAL_FUNC(buscarDadosPaciente), NULL); w = glade_xml_get_widget (xmlJP, "notebook1"); gtk_signal_connect(GTK_OBJECT(w),"switch_page", GTK_SIGNAL_FUNC(alteraEnabledBotoes), NULL); w = glade_xml_get_widget (xmlJP, "tabelaExames"); gtk_signal_connect(GTK_OBJECT(w),"select_row", GTK_SIGNAL_FUNC(selecionaDadosTabelaExame), NULL); //permitir que o edit fique desabilitado w = glade_xml_get_widget(xmlJRE,"entryEtiqueta"); gtk_widget_set(w,"editable",false); //permitir que o botão fique desabilitado w = glade_xml_get_widget(xmlJP,"buttonNovo"); gtk_widget_set(w,"sensitive",false); pthread_t threadOuvir; int status; int i =0; int rc = pthread_create(&threadOuvir, NULL, ouvirCliente, (void *)i) ; if (rc){ printf("ERRO no retorno função pthread_create() %d\n", rc); exit(-1); }//if gtk_main(); // O Loop de eventos do GTK gdk_threads_leave(); return 0; } //encerra conexão com banco de dados e a aplicação void exit_nicely(){
93
PQfinish(conn); exit(1); } char* getEndIPLocal(){ #ifndef unix WORD wVersionRequested; WSADATA wsaData; PHOSTENT hostinfo; wVersionRequested = MAKEWORD( 1, 1 ); #endif #ifdef unix struct hostent *hostinfo; #endif char name[255]; char *ip; #ifndef unix if ( WSAStartup( wVersionRequested, &wsaData ) == 0 ) #endif if( gethostname ( name, sizeof(name)) == 0) { if((hostinfo = gethostbyname(name)) != NULL) { int nCount = 0; #ifndef unix while(hostinfo->h_addr_list[nCount]){ ip = inet_ntoa(*(struct in_addr *)hostinfo->h_addr_list[nCount]); nCount++; } #endif #ifdef unix ip = inet_ntoa(*((struct in_addr *)hostinfo->h_addr)); #endif } } return ip; } #ifndef unix void enviarMensagem(SOCKET s, string msg){ int bytesSent; const char* sendbuf = msg.c_str(); bytesSent = send( s, sendbuf, strlen(sendbuf), 0 ); closesocket(s); } #endif #ifdef unix void enviarMensagemUnix(int s, string msg){ int bytesSent; const char* sendbuf = msg.c_str();
94
bytesSent = send( s, sendbuf, strlen(sendbuf), 0 ); close(s); } #endif void enviarParaCliente(string msg){ #ifndef unix WSADATA wsaData; int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); if ( iResult != NO_ERROR ) printf("Erro no WSAStartup()\n"); SOCKET m_socketEnvio; #endif #ifdef unix int m_socketEnvio; #endif m_socketEnvio = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); #ifndef unix if ( m_socketEnvio == INVALID_SOCKET ) { WSACleanup(); } #endif #ifdef unix if ( m_socketEnvio == -1 ) { } #endif sockaddr_in clientService; clientService.sin_family = AF_INET; clientService.sin_addr.s_addr = inet_addr( getEndIPLocal()); clientService.sin_port = htons( 27014 ); #ifndef unix if ( connect( m_socketEnvio, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) { WSACleanup(); MessageBox(NULL,"Não foi possível estabelecer conexão com o cliente", "GE HL7",MB_ICONSTOP|MB_OK); }//if #endif #ifdef unix if ( connect( m_socketEnvio, (struct sockaddr*) &clientService, sizeof(clientService) ) == -1) { printf( "Falha na conexao...%s[%d]\n", strerror(errno),errno); }//if #endif else{ #ifndef unix enviarMensagem(m_socketEnvio, msg); #endif
95
#ifdef unix enviarMensagemUnix(m_socketEnvio,msg); #endif }//else } void updatePaciente(string id_paciente, string flag){ string sql = "UPDATE paciente SET tem_cad_paciente = '"+ flag+"' WHERE id_paciente_interno = '"+id_paciente+"'"; PGresult *res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) == PGRES_FATAL_ERROR){ fprintf(stderr, "Erro durante update da tabela paciente!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if } bool pacienteJaTemCadastro(string id_paciente){ if (conn == NULL) conectarBanco(); string sql = "Select id_paciente_interno from paciente where id_paciente_interno = '"+id_paciente+"'"; PGresult *res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK){ fprintf(stderr,"Erro durante a selecao tipo_exame para salvar resultado!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if //se existe paciente então devo setar o campo ja_tem_cadastro para "S" if (PQntuples(res)>0){ updatePaciente(id_paciente,"S"); return true; } else return false; } string criarBuscaParaCadastroPaciente(string id_paciente){ return "GerenciaExames|BuscaPaciente|CadastroPaciente|"+id_paciente+"|DEM|"; } string criarResultadoParaProntuario(string id_paciente,string flag, string cod_exame){ if (conn == NULL) conectarBanco(); string sql; if (flag=="N") { sql = "Select resultado.*, tipo_exame.nome_exame from tipo_exame,exame inner join resultado on(resultado.cod_etiqueta = exame.id_etiqueta) where id_paciente_interno = '"+ id_paciente+"' and exame.cod_tipo_exame = tipo_exame.cod_exame"; }//if else {
96
sql = "Select resultado.*, tipo_exame.nome_exame from tipo_exame,exame inner join resultado on(resultado.cod_etiqueta = exame.id_etiqueta) where id_paciente_interno = '"+ id_paciente+"' and exame.cod_tipo_exame = cod_exame and resultado.cod_etiqueta = '"+cod_exame+"'"; } PGresult *res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK){ fprintf(stderr, "Erro durante a selecao ddos dados para criar msg PEP\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if da exceção sql char quant[4]; itoa(PQntuples(res),quant,10); string msgPEP = "GerenciaExames|ResultadoExame|ProntuarioEletronico|"+ id_paciente+"|RES |"+(string) quant+"|"; int leucocitos_fnum = PQfnumber(res, "leucocitos"); int eritrocitos_fnum = PQfnumber(res, "eritrocitos"); int hematocitos_fnum = PQfnumber(res, "hematocitos"); int hemoglobina_fnum = PQfnumber(res, "hemoglobina"); int colesterol_fnum = PQfnumber(res, "colesterol"); int glicose_fnum = PQfnumber(res, "glicose"); int creatina_fnum = PQfnumber(res, "creatina"); int sodio_fnum = PQfnumber(res, "sodio"); int potassio_fnum = PQfnumber(res, "potassio"); int calcio_fnum = PQfnumber(res, "calcio"); int magnesio_fnum = PQfnumber(res, "magnesio"); int fosforo_fnum = PQfnumber(res, "fosforo"); int ph_fnum = PQfnumber(res, "ph"); int densidade_fnum = PQfnumber(res, "densidade"); int hemacias_fnum = PQfnumber(res, "hemacias"); int proteinas_fnum = PQfnumber(res, "proteinas"); int bacterioscopia_fnum = PQfnumber(res, "bacterioscopia"); int ldl_fnum = PQfnumber(res, "colesterol_ldl"); int hdl_fnum = PQfnumber(res, "colesterol_hdl"); int vldl_fnum = PQfnumber(res, "colesterol_vldl"); int exame_fnum = PQfnumber(res, "nome_exame"); char *nome_exame, *colesterol_vldl, *colesterol_hdl, *colesterol_ldl, *bacterioscopia, *proteinas, *hemacias,*densidade, *ph, *fosforo, *magnesio, *calcio, *potassio, *sodio, *creatina, *glicose, *colesterol, *hemoglobina, *hematocitos, *eritrocitos, *leucocitos; //percorre todas as linhas e verifica o tipo de exame antes de gerar string //contendo resultado for (int i = 0; i < PQntuples(res);i++) { nome_exame = PQgetvalue(res, i, exame_fnum); string tipo_exame = (string)nome_exame; if (tipo_exame == "Hemograma Completo") { leucocitos = PQgetvalue(res, i, leucocitos_fnum); eritrocitos = PQgetvalue(res, i, eritrocitos_fnum); hematocitos = PQgetvalue(res, i, hematocitos_fnum); hemoglobina = PQgetvalue(res, i, hemoglobina_fnum);
97
colesterol = PQgetvalue(res, i, colesterol_fnum); glicose = PQgetvalue(res, i, glicose_fnum); creatina = PQgetvalue(res, i, creatina_fnum); sodio = PQgetvalue(res, i,sodio_fnum); potassio = PQgetvalue(res, i, potassio_fnum); calcio = PQgetvalue(res, i, calcio_fnum); magnesio = PQgetvalue(res, i, magnesio_fnum); fosforo = PQgetvalue(res, i, fosforo_fnum); string dados = "Leucocitos:["+(string)leucocitos+"] Eritrocitos:["+ (string)eritrocitos+"] Hematocitos:["+ (string)hematocitos+"] Hemoglobina:["+ (string)hemoglobina+"] Colesterol:["+ (string)colesterol+"] Glicose:["+ (string)glicose+"] Creatina:["+ (string)creatina+"] Sodio:["+ (string)sodio+"] Potassio:["+ (string)potassio+"] Calcio:["+ (string)calcio+"] Magnesio:["+ (string)magnesio+"] Fosforo:["+ (string)fosforo+"])|"; msgPEP = msgPEP+"Hemograma Completo ("+dados; }//if if (tipo_exame == "Urina") { ph = PQgetvalue(res, i, ph_fnum); densidade = PQgetvalue(res, i, densidade_fnum); hemacias = PQgetvalue(res, i, hemacias_fnum); proteinas = PQgetvalue(res, i, proteinas_fnum); bacterioscopia = PQgetvalue(res, i, bacterioscopia_fnum); glicose = PQgetvalue(res, i, glicose_fnum); string dados = "PH:["+(string)ph+"] Densidade:["+ (string)densidade+"] Hemacias:["+ (string)hemacias+"] Proteinas:["+ (string)proteinas+"] Glicose:["+ (string)glicose+"] Bacterioscopia:["+ (string)bacterioscopia+"])|"; msgPEP = msgPEP+"Urina ("+dados; }//if if(tipo_exame == "Colesterol"){ colesterol_ldl = PQgetvalue(res, i, ldl_fnum); colesterol_hdl = PQgetvalue(res, i, hdl_fnum); colesterol_vldl = PQgetvalue(res, i, vldl_fnum); string dados = "Colesterol LDL:["+ (string)colesterol_ldl+"] Colesterol HDL:["+ (string)colesterol_hdl+"] Colesterol VLDL:["+ (string)colesterol_vldl+"])|"; msgPEP = msgPEP+"Colesterol ("+dados; }//if }//for return msgPEP; } void excluirDaBusca(string id_paciente){ string sql = "Delete from busca where id_paciente = '"+id_paciente+"'"; PGresult *res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) == PGRES_FATAL_ERROR)
98
{ fprintf(stderr, "Erro durante a exclusao de busca!\n",PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if } string alterarFormatoDataNascimento(string nascimento){ string ano = nascimento.substr(0,4); string mes = nascimento.substr(4,2); string dia = nascimento.substr(6,2); string hora = nascimento.substr(8,2); string min = nascimento.substr(10,2); return dia+"/"+mes+"/"+ano+" "+hora+":"+min; } void *receberMensagem(void *s){ #ifndef unix SOCKET soc = (SOCKET) s; #endif #ifdef unix int soc = (int) s; #endif int bytesRecv = -1; char recvbuf[750] = ""; bytesRecv = recv( soc, recvbuf, 750, 0 ); if (bytesRecv > 0) { char * msg = recvbuf; string primeiroCampo = strtok(msg,"|"); string token = strtok(NULL,"|"); if (token == "ADT") {//recebida mensagem de inclusao de Paciente token = strtok(NULL,"|");//pula dataHoraMsg token = strtok(NULL,"|");//pula aplicRemetente string id_paciente_interno = strtok(NULL,"|"); //se jah estah cadastrado entao tem_cadastro = S, se naum vou //incluir na base if (pacienteJaTemCadastro(id_paciente_interno)==false) { string nome_paciente = strtok(NULL,"|"); string nascimento = strtok(NULL,"|"); string data_nascimento=alterarFormatoDataNascimento(nascimento); string sexo = strtok(NULL,"|"); inserirDadosTabelaPaciente(id_paciente_interno,nome_paciente, data_nascimento,sexo,"S"); }//if }//if if (token== "ACK") {//recebida mensagem de confirmação MessageBox(NULL,"Recebeu confirmacao","Gerencia Exames HL7", MB_ICONINFORMATION|MB_OK); }//if if (token== "RPI") {//mensagem recebida é resultado de uma busca
99
token = strtok(NULL,"|");//pula dataHoraMsg token = strtok(NULL,"|");//pula aplicRemetente token = strtok(NULL,"|"); string id_paciente_interno = token; token = strtok(NULL,"|"); /*campo referente ao status da consulta, se for NF é porque p paciente não se encontra cadastrado na base do aplicativo Cadastropaciente, se não, retorna OK*/ if (token != "NF") { updatePaciente(id_paciente_interno, "S"); string msgem = criarResultadoParaProntuario(id_paciente_interno,"N","0"); enviarParaCliente(msgem); }//if excluirDaBusca(id_paciente_interno); }//if token = strtok(NULL,"¨"); //encontrar msgRecebida e adiciona-la no textView da aba Receber token = strtok(NULL,"¨"); string msgRecebida = token; GtkWidget *textview = glade_xml_get_widget(xmlJP,"tvMsgsRecebidas"); GtkTextIter start, end; GtkTextBuffer *buffer= gtk_text_buffer_new(NULL); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); gtk_text_buffer_get_bounds(buffer, &start, &end); char * msgTextView = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); string teste = (string)msgTextView+"\n"+ msgRecebida+"\n************************************"; gtk_text_buffer_set_text(buffer,teste.c_str(), -1); gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview),buffer); }//if } void *ouvirCliente(void * i){ #ifndef unix // Inicializa Winsock. WSADATA wsaData; //inicia o socket, o MAKEWORD(2,2) encontra versão do winsocket int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); if ( iResult != NO_ERROR ){} SOCKET m_socket; #endif #ifdef unix int m_socket; #endif // Cria socket m_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); /*primeiro parametro é a familia, segundo é o tipo(TCP ou UDP e o terceiro é o protocolo associado a familia)*/ #ifndef unix if ( m_socket == INVALID_SOCKET ) {
100
WSACleanup(); printf("socket invalido!\n"); } #endif #ifdef unix if ( m_socket == -1) {} #endif sockaddr_in service; service.sin_family = AF_INET; service.sin_addr.s_addr = inet_addr(getEndIPLocal()); service.sin_port = htons(27010);//porta em que o aplicativo estará "ouvindo" //bind associa o socket ao endereço #ifndef unix if (bind(m_socket,(SOCKADDR*)&service, sizeof(service) ) == SOCKET_ERROR ) { closesocket(m_socket); printf("erro no bind!\n"); } #endif #ifdef unix if ( bind( m_socket, (struct sockaddr*)&service, sizeof(service) ) == -1 ) { close(m_socket); } #endif //lista o numero max de conexões aceitas pelo servidor if ( listen( m_socket, 1 ) == -1 ){} #ifndef unix SOCKET AcceptSocket; #endif #ifdef unix int AcceptSocket; #endif pthread_t threadCliente; int status; while (true) { //Aguarda por novas conexões AcceptSocket = accept( m_socket, NULL, NULL ); if (AcceptSocket != -1) { int rc = pthread_create(&threadCliente, NULL, receberMensagem, (void *)AcceptSocket) ; rc = pthread_join(threadCliente, (void **)&status); if (rc){ exit(-1); }//if #ifndef unix closesocket(AcceptSocket); #endif #ifdef unix close(AcceptSocket); #endif }//if
101
}//while } bool jaFezBuscaEmCadastroPaciente(string id_paciente){ string sql = "Select * from busca where id_paciente = '"+id_paciente+"'"; PGresult *res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr,"Erro durante selecao de dados em busca\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if if (PQntuples(res)>0) { return true; }//if else { return false; }//else } void armazenarNaBusca(string id,string cod_exame){ string sql = "Insert into busca (id_paciente, cod_etiqueta_exame) values ('"+id+"', '"+cod_exame+"')"; PGresult *res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) == PGRES_FATAL_ERROR){ fprintf(stderr, "Erro durante inserção de dados em busca!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if } void salvarResultadoExame(){ PGresult *res ; string sql; w = glade_xml_get_widget(xmlJRE,"entryTipoExameJRE"); string nome_exame = gtk_entry_get_text(GTK_ENTRY(w)); sql = "Select cod_exame from tipo_exame,paciente where nome_exame = '"+ nome_exame+"'"; res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK){ fprintf(stderr,"Erro durante selecao tipo_exame para salvar resultado!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if int cod_exame_fnum = PQfnumber(res, "cod_exame"); char* cod_exame = PQgetvalue(res, 0, cod_exame_fnum); w = glade_xml_get_widget(xmlJRE,"entryCodEtiquetaJRE"); string cod_etiqueta = gtk_entry_get_text(GTK_ENTRY(w)); if (nome_exame=="Hemograma Completo")
102
{ w = glade_xml_get_widget(xmlJRE,"entryHematocitos"); string hematocitos = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryEritrocitos"); string eritrocitos = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryLeucocitos"); string leucocitos = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryHemoglobina"); string hemoglobina = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryColesterol"); string colesterol = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryGlicose"); string glicose = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryCreatina"); string creatina = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entrySodio"); string sodio = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryPotassio"); string potassio = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryCalcio"); string calcio = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryMagnesio"); string magnesio = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryFosforo"); string fosforo = gtk_entry_get_text(GTK_ENTRY(w)); sql = "Insert into resultado (cod_etiqueta, hematocitos, eritrocitos, leucocitos, hemoglobina, colesterol, glicose, creatina, sodio, potassio, calcio, magnesio, fosforo) values ('"+ cod_etiqueta+"', '"+hematocitos+"', '"+ eritrocitos+"', '"+leucocitos+"', '"+ hemoglobina+"', '"+colesterol+"', '"+ glicose+"', '"+creatina+"', '"+sodio+"', '"+ potassio+"', '"+calcio+"', '"+ magnesio+"', '"+fosforo+"')"; }//hemograma if (nome_exame=="Urina") { w = glade_xml_get_widget(xmlJRE,"entryPH"); string ph = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryDensidade"); string densidade = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryHemacias"); string hemacias = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryProteinas"); string proteinas = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryGlicoseUrina"); string glicose = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"tvBacterioscopia"); GtkTextIter start, end; GtkTextBuffer *buffer= gtk_text_buffer_new(NULL); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w)); gtk_text_buffer_get_bounds(buffer, &start, &end); char * result = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); string bacterioscopia = result; sql = "Insert into resultado (cod_etiqueta, ph, densidade, hemacias, proteinas, glicose, bacterioscopia) values ('"+ cod_etiqueta+"', '"+ph+"', '"+densidade+"', '"+hemacias+"', '"+
103
proteinas+"', '"+glicose+"', '"+bacterioscopia+"')"; }//urina if(nome_exame=="Colesterol"){ w = glade_xml_get_widget(xmlJRE,"entryLDL"); string ldl = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryHDL"); string hdl = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJRE,"entryVLDL"); string vldl = gtk_entry_get_text(GTK_ENTRY(w)); sql = "Insert into resultado (cod_etiqueta, colesterol_ldl, colesterol_hdl, colesterol_vldl) values ('"+ cod_etiqueta+"', '"+ldl+"', '"+hdl+"', '"+vldl+"')"; }//colesterol res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) == PGRES_FATAL_ERROR) { fprintf(stderr, "Erro durante a inserção dos dados na tabela paciente!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if w = glade_xml_get_widget(xmlJP,"entryIdInternoBusca"); string id = gtk_entry_get_text(GTK_ENTRY(w)); sql = "Select tem_cad_paciente from paciente where id_paciente_interno = '" +id+"'"; res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "Erro durante a selecao tem_cad_paciente!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if int tem_cad_fnum = PQfnumber(res, "tem_cad_paciente"); char* tem_cad_paciente = PQgetvalue(res, 0, tem_cad_fnum); string cadPaciente = (string)tem_cad_paciente; if (cadPaciente == "S") { string mensagem = criarResultadoParaProntuario(id,"S",cod_etiqueta); enviarParaCliente(mensagem); } else { if (jaFezBuscaEmCadastroPaciente(id)==false) { armazenarNaBusca(id,string(cod_exame)); string mensagem = criarBuscaParaCadastroPaciente(id); enviarParaCliente(mensagem); } } fecharJanelaRE(); } void limparCamposResultadoExame(int page){ if (page == 0) { w = glade_xml_get_widget(xmlJRE,"entryLeucocitos");
104
gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryEritrocitos"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryHematocitos"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryHemoglobina"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryColesterol"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryGlicose"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryCreatina"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entrySodio"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryPotassio"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryCalcio"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryMagnesio"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryFosforo"); gtk_entry_set_text(GTK_ENTRY(w),""); //desabilitar as tabs <> de tipoExame w = glade_xml_get_widget(xmlJRE,"hpanedHemograma"); gtk_widget_set(w,"visible",true); w = glade_xml_get_widget(xmlJRE,"hpanedUrina"); gtk_widget_set(w,"visible",false); w = glade_xml_get_widget(xmlJRE,"hpanedColesterol"); gtk_widget_set(w,"visible",false); }//if if (page==1) { w = glade_xml_get_widget(xmlJRE,"entryPH"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryDensidade"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryHemacias"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryProteinas"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryGlicoseUrina"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"tvBacterioscopia"); //para limpar o textview GtkTextIter start, end; GtkTextBuffer *buffer= gtk_text_buffer_new(NULL); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w)); gtk_text_buffer_get_bounds(buffer, &start, &end); gtk_text_buffer_set_text(buffer,"", -1); gtk_text_view_set_buffer(GTK_TEXT_VIEW(w),buffer); //desabilitar as tabs <> de tipoExame w = glade_xml_get_widget(xmlJRE,"hpanedHemograma"); gtk_widget_set(w,"visible",false); w = glade_xml_get_widget(xmlJRE,"hpanedUrina"); gtk_widget_set(w,"visible",true);
105
w = glade_xml_get_widget(xmlJRE,"hpanedColesterol"); gtk_widget_set(w,"visible",false); }//if if (page==2) { w = glade_xml_get_widget(xmlJRE,"entryLDL"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryHDL"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJRE,"entryVLDL"); gtk_entry_set_text(GTK_ENTRY(w),""); //desabilitar as tabs <> de tipoExame w = glade_xml_get_widget(xmlJRE,"hpanedHemograma"); gtk_widget_set(w,"visible",false); w = glade_xml_get_widget(xmlJRE,"hpanedUrina"); gtk_widget_set(w,"visible",false); w = glade_xml_get_widget(xmlJRE,"hpanedColesterol"); gtk_widget_set(w,"visible",true); }//if } void preencherDadosResultadoHemograma(PGresult *res){ int leucocitos_fnum = PQfnumber(res, "leucocitos"); int eritrocitos_fnum = PQfnumber(res, "eritrocitos"); int hematocitos_fnum = PQfnumber(res, "hematocitos"); int hemoglobina_fnum = PQfnumber(res, "hemoglobina"); int colesterol_fnum = PQfnumber(res, "colesterol"); int glicose_fnum = PQfnumber(res, "glicose"); int creatina_fnum = PQfnumber(res, "creatina"); int sodio_fnum = PQfnumber(res, "sodio"); int potassio_fnum = PQfnumber(res, "potassio"); int calcio_fnum = PQfnumber(res, "calcio"); int magnesio_fnum = PQfnumber(res, "magnesio"); int fosforo_fnum = PQfnumber(res, "fosforo"); char *leucocitos, *eritrocitos, *hematocitos, *hemoglobina, *colesterol, *glicose, *creatina, *sodio,*potassio, *calcio, *magnesio, *fosforo; leucocitos = PQgetvalue(res, 0, leucocitos_fnum); eritrocitos = PQgetvalue(res, 0, eritrocitos_fnum); hematocitos = PQgetvalue(res, 0, hematocitos_fnum); hemoglobina = PQgetvalue(res, 0, hemoglobina_fnum); colesterol = PQgetvalue(res, 0, colesterol_fnum); glicose = PQgetvalue(res, 0, glicose_fnum); creatina = PQgetvalue(res, 0, creatina_fnum); sodio = PQgetvalue(res, 0, sodio_fnum); potassio = PQgetvalue(res, 0, potassio_fnum); calcio = PQgetvalue(res, 0, calcio_fnum); magnesio = PQgetvalue(res, 0, magnesio_fnum); fosforo = PQgetvalue(res, 0, fosforo_fnum); w = glade_xml_get_widget(xmlJRE,"entryLeucocitos"); gtk_entry_set_text(GTK_ENTRY(w),leucocitos); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryEritrocitos"); gtk_entry_set_text(GTK_ENTRY(w),eritrocitos); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryHematocitos"); gtk_entry_set_text(GTK_ENTRY(w),hematocitos);
106
gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryHemoglobina"); gtk_entry_set_text(GTK_ENTRY(w),hemoglobina); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryColesterol"); gtk_entry_set_text(GTK_ENTRY(w),colesterol); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryGlicose"); gtk_entry_set_text(GTK_ENTRY(w),glicose); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryCreatina"); gtk_entry_set_text(GTK_ENTRY(w),creatina); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entrySodio"); gtk_entry_set_text(GTK_ENTRY(w),sodio); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryPotassio"); gtk_entry_set_text(GTK_ENTRY(w),potassio); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryCalcio"); gtk_entry_set_text(GTK_ENTRY(w),calcio); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryMagnesio"); gtk_entry_set_text(GTK_ENTRY(w),magnesio); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryFosforo"); gtk_entry_set_text(GTK_ENTRY(w),fosforo); gtk_widget_set(w,"editable",false); } void preencherDadosResultadoUrina(PGresult *res){ int ph_fnum = PQfnumber(res, "ph"); int densidade_fnum = PQfnumber(res, "densidade"); int hemacias_fnum = PQfnumber(res, "hemacias"); int proteinas_fnum = PQfnumber(res, "proteinas"); int glicose_fnum = PQfnumber(res, "glicose"); int bacterioscopia_fnum = PQfnumber(res, "bacterioscopia"); char* ph,*densidade, *hemacias, *proteinas, *glicose, *bacterioscopia; ph = PQgetvalue(res, 0, ph_fnum); densidade = PQgetvalue(res, 0, densidade_fnum); hemacias = PQgetvalue(res, 0, hemacias_fnum); proteinas = PQgetvalue(res, 0, proteinas_fnum); glicose = PQgetvalue(res, 0, glicose_fnum); bacterioscopia = PQgetvalue(res, 0, bacterioscopia_fnum); w = glade_xml_get_widget(xmlJRE,"entryPH"); gtk_entry_set_text(GTK_ENTRY(w),ph); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryDensidade"); gtk_entry_set_text(GTK_ENTRY(w),densidade); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryHemacias"); gtk_entry_set_text(GTK_ENTRY(w),hemacias); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryProteinas"); gtk_entry_set_text(GTK_ENTRY(w),proteinas); gtk_widget_set(w,"editable",false);
107
w = glade_xml_get_widget(xmlJRE,"entryGlicoseUrina"); gtk_entry_set_text(GTK_ENTRY(w),glicose); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"tvBacterioscopia"); GtkTextBuffer *buffer= gtk_text_buffer_new(NULL); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w)); gtk_text_buffer_set_text(buffer,bacterioscopia, -1); gtk_text_view_set_buffer(GTK_TEXT_VIEW(w),buffer); gtk_widget_set(w,"editable",false); } void preencherDadosResultadoColesterol(PGresult *res){ int ldl_fnum = PQfnumber(res, "colesterol_ldl"); int hdl_fnum = PQfnumber(res, "colesterol_hdl"); int vldl_fnum = PQfnumber(res, "colesterol_vldl"); char *ldl,*hdl, *vldl; ldl = PQgetvalue(res, 0, ldl_fnum); hdl = PQgetvalue(res, 0, hdl_fnum); vldl = PQgetvalue(res, 0, vldl_fnum); w = glade_xml_get_widget(xmlJRE,"entryLDL"); gtk_entry_set_text(GTK_ENTRY(w),ldl); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryHDL"); gtk_entry_set_text(GTK_ENTRY(w),hdl); gtk_widget_set(w,"editable",false); w = glade_xml_get_widget(xmlJRE,"entryVLDL"); gtk_entry_set_text(GTK_ENTRY(w),vldl); gtk_widget_set(w,"editable",false); } void selecionarDadosResultado(){ PGresult *res; string sql="SELECT * FROM exame, resultado,tipo_exame WHERE id_etiqueta = '"+ (string)id_etiqueta_ResulExame+"' and id_etiqueta = cod_etiqueta and cod_tipo_exame = cod_exame"; res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "Erro durante a seleçao de exameXresultado!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if if (PQntuples(res)>0) { int dtColeta_fnum = PQfnumber(res, "data_coleta"); int etiqueta_fnum = PQfnumber(res, "id_etiqueta"); int exame_fnum = PQfnumber(res, "nome_exame"); char* nome_exame,*id_etiqueta, *data_coleta; nome_exame = PQgetvalue(res, 0, exame_fnum); id_etiqueta = PQgetvalue(res, 0, etiqueta_fnum); data_coleta = PQgetvalue(res, 0, dtColeta_fnum); w = glade_xml_get_widget(xmlJRE,"entryTipoExameJRE"); gtk_entry_set_text(GTK_ENTRY(w),nome_exame); w = glade_xml_get_widget(xmlJRE,"entryCodEtiquetaJRE");
108
gtk_entry_set_text(GTK_ENTRY(w),id_etiqueta); w = glade_xml_get_widget(xmlJRE,"entryDataColetaJRE"); gtk_entry_set_text(GTK_ENTRY(w),data_coleta); string exame = nome_exame; /* verifica o tipo de exame do resultado e limpa os campos e habilita page do referente ao tipo de exame*/ if (exame == "Hemograma Completo") { limparCamposResultadoExame(0); preencherDadosResultadoHemograma(res); }//hemograma if(exame == "Urina") { limparCamposResultadoExame(1); preencherDadosResultadoUrina(res); }//urina if(exame == "Colesterol") { limparCamposResultadoExame(2); preencherDadosResultadoColesterol(res); }//colesterol }//if else { sql = "SELECT * FROM exame,tipo_exame where id_etiqueta = '"+( string)id_etiqueta_ResulExame+"' and cod_tipo_exame=cod_exame"; res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr,"Erro durante seleçao exame ao compor novo resultado!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); } //if int dtColeta_fnum = PQfnumber(res, "data_coleta"); int etiqueta_fnum = PQfnumber(res, "id_etiqueta"); int exame_fnum = PQfnumber(res, "nome_exame"); char* nome_exame,*id_etiqueta, *data_coleta; nome_exame = PQgetvalue(res, 0, exame_fnum); id_etiqueta = PQgetvalue(res, 0, etiqueta_fnum); data_coleta = PQgetvalue(res, 0, dtColeta_fnum); w = glade_xml_get_widget(xmlJRE,"entryTipoExameJRE"); gtk_entry_set_text(GTK_ENTRY(w),nome_exame); w = glade_xml_get_widget(xmlJRE,"entryCodEtiquetaJRE"); gtk_entry_set_text(GTK_ENTRY(w),id_etiqueta); w = glade_xml_get_widget(xmlJRE,"entryDataColetaJRE"); gtk_entry_set_text(GTK_ENTRY(w),data_coleta); string exame = nome_exame; if (exame == "Hemograma Completo") limparCamposResultadoExame(0); if (exame == "Urina") limparCamposResultadoExame(1); if (exame == "Colesterol") limparCamposResultadoExame(2); } //else
109
} void fecharJanelaRE(){ gtk_widget_hide(janelaResultadoExame); } void verResultadoExame(){ if ((id_etiqueta_ResulExame == " " )or(id_etiqueta_ResulExame == NULL)) { printf("Voce deve selecionar um exame para a visualização do resultado!\n"); }//if else { if (janelaResultadoExame == NULL) { xmlJRE = glade_xml_new("interface/interface.glade", "JanelaResultadoExame",NULL); w = glade_xml_get_widget (xmlJRE, "buttonSalvarResultado"); gtk_signal_connect(GTK_OBJECT(w),"clicked", GTK_SIGNAL_FUNC(salvarResultadoExame), NULL); janelaResultadoExame = glade_xml_get_widget(xmlJRE, "JanelaResultadoExame"); w = glade_xml_get_widget (xmlJRE, "buttonSairResultadoExame"); gtk_signal_connect(GTK_OBJECT(w),"clicked", GTK_SIGNAL_FUNC(fecharJanelaRE),NULL); gtk_signal_connect(GTK_OBJECT(janelaResultadoExame),"destroy", GTK_SIGNAL_FUNC(fecharJanela),janelaResultadoExame); }//if selecionarDadosResultado(); gtk_widget_show(janelaResultadoExame); }//else } void selecionaDadosTabelaExame(GtkWidget *clist, gint row, gint column, GdkEventButton *event, gpointer data){ gtk_clist_get_text(GTK_CLIST(clist), row, 0, &id_etiqueta_ResulExame); } void buscarExamesCadastrados(){ GtkWidget* tabela = glade_xml_get_widget (xmlJP, "tabelaExames"); gtk_clist_clear(GTK_CLIST(tabela)); ; if (conn == NULL) conectarBanco(); PGresult *res; w = glade_xml_get_widget(xmlJP,"entryIdInternoBusca"); string idInterno = gtk_entry_get_text(GTK_ENTRY(w)); string sql = "SELECT id_etiqueta, data_coleta,nome_exame,urgencia FROM exame, tipo_exame WHERE id_paciente_interno = '"+idInterno+"' and cod_tipo_exame = cod_exame"; res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "Erro durante a seleçao de exameXtipo_exame!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if
110
int etiqueta_fnum = PQfnumber(res, "id_etiqueta"); int coleta_fnum = PQfnumber(res, "data_coleta"); int exame_fnum = PQfnumber(res, "nome_exame"); int urgencia_fnum = PQfnumber(res, "urgencia"); char *id_etiqueta, *data_coleta, *nome_exame, *urgencia; int linha = PQntuples(res); if (linha > 0) { gchar* dados[linha][4]; for (int i = 0; i < linha;i++) { id_etiqueta = PQgetvalue(res, i, etiqueta_fnum); data_coleta = PQgetvalue(res, i, coleta_fnum); nome_exame = PQgetvalue(res, i, exame_fnum); urgencia = PQgetvalue(res, i, urgencia_fnum); dados[i][0] = id_etiqueta; dados[i][1]= data_coleta; dados[i][2] = nome_exame; dados[i][3]= urgencia; gint row = gtk_clist_append( GTK_CLIST (tabela), dados[i]); }//for }//if else { printf("Nenhum exame foi cadastrado para este paciente\n"); }//else } void alteraEnabledBotoes(GtkNotebook *notebook, GtkNotebookPage *page, gint page_num, gpointer user_data){ if (page_num == 0) { w = glade_xml_get_widget(xmlJP,"buttonNovo"); gtk_widget_set(w,"sensitive",false); w = glade_xml_get_widget(xmlJP,"buttonBuscar"); gtk_widget_set(w,"sensitive",true); }//if if (page_num==1) { w = glade_xml_get_widget(xmlJP,"buttonNovo"); gtk_widget_set(w,"sensitive",true); w = glade_xml_get_widget(xmlJP,"buttonBuscar"); gtk_widget_set(w,"sensitive",false); }//if if(page_num==2) { w = glade_xml_get_widget(xmlJP,"buttonNovo"); gtk_widget_set(w,"sensitive",true); w = glade_xml_get_widget(xmlJP,"buttonBuscar"); gtk_widget_set(w,"sensitive",true); } } static gboolean buscarDadosPaciente(){ w = glade_xml_get_widget(xmlJP,"entryIDInterno"); string idInterno = gtk_entry_get_text(GTK_ENTRY(w));
111
if (conn == NULL) conectarBanco(); PGresult *res; string sql = "SELECT nome_completo,sexo,data_nascimento,tem_cad_paciente FROM paciente WHERE id_paciente_interno = '"+idInterno+"'"; res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "Erro durante a seleçao dos dados da tabela exames!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if if (PQntuples(res)>0) { int nome_completo_fnum = PQfnumber(res, "nome_completo"); int sexo_fnum = PQfnumber(res, "sexo"); int data_nascimento_fnum = PQfnumber(res, "data_nascimento"); int tem_cad_paciente_fnum = PQfnumber(res, "tem_cad_paciente"); char *nome_completo, *sexo, *data_nascimento; nome_completo = PQgetvalue(res, 0, nome_completo_fnum); sexo = PQgetvalue(res, 0, sexo_fnum); data_nascimento = PQgetvalue(res, 0, data_nascimento_fnum); tem_cad_paciente = PQgetvalue(res, 0, tem_cad_paciente_fnum); w = glade_xml_get_widget(xmlJP,"entryNome"); gtk_entry_set_text(GTK_ENTRY(w),nome_completo); GtkComboBox *combo = GTK_COMBO_BOX (glade_xml_get_widget(xmlJP,"cbSexo")); if (sexo == "F") { gtk_combo_box_set_active (GTK_COMBO_BOX (combo),0); }//if else { gtk_combo_box_set_active (GTK_COMBO_BOX (combo),1); }//else string token = strtok(data_nascimento,"/"); w = glade_xml_get_widget(xmlJP,"entryDiaN"); gtk_entry_set_text(GTK_ENTRY(w),token.c_str()); token = strtok(NULL,"/"); w = glade_xml_get_widget(xmlJP,"entryMesN"); gtk_entry_set_text(GTK_ENTRY(w),token.c_str()); token = strtok(NULL," "); w = glade_xml_get_widget(xmlJP,"entryAnoN"); gtk_entry_set_text(GTK_ENTRY(w),token.c_str()); token = strtok(NULL,":"); w = glade_xml_get_widget(xmlJP,"entryHoraN"); gtk_entry_set_text(GTK_ENTRY(w),token.c_str()); token = strtok(NULL," "); w = glade_xml_get_widget(xmlJP,"entryMinN"); gtk_entry_set_text(GTK_ENTRY(w),token.c_str()); }//if return FALSE; } void consultaExame(){ if (conn == NULL)
112
conectarBanco(); w = glade_xml_get_widget(xmlJCE,"entryConsultaExame"); string exame = gtk_entry_get_text(GTK_ENTRY(w)); PGresult* res = selectTipoExame(exame); colocaDadosTabelaTipoExame(res); } void fecharJanela(GtkWidget* janela){ gtk_widget_hide(janela); } void conectarBanco(){ conn = PQsetdbLogin("localhost", "5432", NULL, NULL, "CadastroExame", "postgres", "phigres*"); if (PQstatus(conn) != CONNECTION_OK) { fprintf(stderr, "Conexao com database falhou: %s",PQerrorMessage(conn)); exit_nicely(); } } PGresult* selectTipoExame(string tipoExame){ PGresult *res; string sql = "SELECT * FROM tipo_exame WHERE nome_exame = '"+ tipoExame+"' or nome_exame like '%"+ tipoExame+"%' or nome_exame like '%"+ tipoExame+"' or nome_exame like '"+tipoExame+"%'"; res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "Erro durante a seleçao de nome_exame!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); } return res; } void colocaDadosTabelaTipoExame(PGresult* res){ int cod_exame = PQfnumber(res, "cod_exame"); int nome_exame = PQfnumber(res, "nome_exame"); char *codExame, *nomeExame; int linha = PQntuples(res); gchar* dados[linha][2]; for (int i = 0; i < linha;i++) { codExame = PQgetvalue(res, i, cod_exame); nomeExame = PQgetvalue(res, i, nome_exame); dados[i][0] = codExame; dados[i][1]= nomeExame; w = glade_xml_get_widget (xmlJCE, "tabelaTipoExame");
113
gint row = gtk_clist_append( GTK_CLIST (w), dados[i]); }//for } void selecionaCodTabelaTipoExame(GtkWidget *clist, gint row, gint column, GdkEventButton *event, gpointer data){ gchar *text; gtk_clist_get_text(GTK_CLIST(clist), row, 0, &text); w = glade_xml_get_widget(xmlJP,"entryExame"); gtk_entry_set_text(GTK_ENTRY(w),text); fecharJanela(janelaConsultaExame); } void buscarExame(){ w = glade_xml_get_widget(xmlJP,"notebook1"); gtk_notebook_set_current_page (GTK_NOTEBOOK(w),1); } void buscarTipoExame(){ if (janelaConsultaExame == NULL) { xmlJCE = glade_xml_new("interface/interface.glade","JanelaConsultaExame", NULL); w = glade_xml_get_widget (xmlJCE, "buttonConsultaExame"); gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(consultaExame), NULL); w = glade_xml_get_widget (xmlJCE, "buttonSairConsultaExame"); gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(fecharJanela), janelaConsultaExame); w = glade_xml_get_widget (xmlJCE, "tabelaTipoExame"); gtk_signal_connect(GTK_OBJECT(w),"select_row", GTK_SIGNAL_FUNC(selecionaCodTabelaTipoExame), NULL); janelaConsultaExame = glade_xml_get_widget(xmlJCE,"JanelaConsultaExame"); gtk_signal_connect(GTK_OBJECT(janelaConsultaExame),"destroy", GTK_SIGNAL_FUNC(fecharJanela),janelaConsultaExame); }//if else { w = glade_xml_get_widget(xmlJCE,"entryConsultaExame"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget (xmlJCE, "tabelaTipoExame"); gtk_clist_clear(GTK_CLIST(w)); }//else gtk_widget_show(janelaConsultaExame); } void novoExame(){ w = glade_xml_get_widget(xmlJP,"notebook1"); gtk_notebook_set_current_page (GTK_NOTEBOOK(w),0); } string formatarData(){ w = glade_xml_get_widget(xmlJP,"entryDiaN"); string diaN = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJP,"entryMesN");
114
string mesN = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJP,"entryAnoN"); string anoN = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJP,"entryHoraN"); string horaN = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJP,"entryMinN"); string minN = gtk_entry_get_text(GTK_ENTRY(w)); string data = diaN+"/"+mesN+"/"+anoN+" "+horaN+":"+minN+" "; return data; } void inserirDadosTabelaPaciente(string id_interno,string nome, string data_nascimento,string sexo, string tem_cadastro){ if (conn == NULL) conectarBanco(); PGresult *res; string sql; sql = "Insert into paciente (id_paciente_interno,nome_completo,sexo,data_nascimento,tem_cad_paciente) VALUES ('"+id_interno+"','"+nome+"','"+sexo+"','"+data_nascimento+"','"+tem_cadastro+"')"; res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) == PGRES_FATAL_ERROR) { fprintf(stderr, "Erro durante a inserção dos dados na tabela paciente!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if } void inserirDadosTabelaExame(){ PGresult *res; string sql; w = glade_xml_get_widget(xmlJP,"entryIDInterno"); string id_interno = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJP,"entryDataColeta"); string data_coleta = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJP,"entryExame"); string cod_exame = gtk_entry_get_text(GTK_ENTRY(w)); GtkComboBox *combo=GTK_COMBO_BOX (glade_xml_get_widget(xmlJP,"cbUrgencia")); string urgencia; int n = gtk_combo_box_get_active (GTK_COMBO_BOX (combo)); if (n==0) urgencia = "S"; if (n==1) urgencia = "N"; sql = "Insert into exame (id_paciente_interno,cod_tipo_exame,urgencia,data_coleta) VALUES('"+id_interno+"','"+cod_exame+"','"+urgencia+"','"+data_coleta+"')"; res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) == PGRES_FATAL_ERROR) { fprintf(stderr, "Erro durante a inserção dos dados na tabela exame!\n", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if
115
} void limparCamposCadastro(){ w = glade_xml_get_widget(xmlJP,"entryIDInterno"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJP,"entryEtiqueta"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJP,"entryDataColeta"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJP,"entryExame"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJP,"entryNome"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJP,"entryDiaN"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJP,"entryMesN"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJP,"entryAnoN"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJP,"entryHoraN"); gtk_entry_set_text(GTK_ENTRY(w),""); w = glade_xml_get_widget(xmlJP,"entryMinN"); gtk_entry_set_text(GTK_ENTRY(w),""); GtkComboBox *combo = GTK_COMBO_BOX (glade_xml_get_widget(xmlJP,"cbSexo")); gtk_combo_box_set_active (GTK_COMBO_BOX (combo),-1); combo = GTK_COMBO_BOX (glade_xml_get_widget(xmlJP,"cbUrgencia")); gtk_combo_box_set_active (GTK_COMBO_BOX (combo),-1); } void salvarExame(){ if (conn == NULL) conectarBanco(); if ((tem_cad_paciente == NULL) or (tem_cad_paciente==" ")) { w = glade_xml_get_widget(xmlJP,"entryIDInterno"); string id_interno = gtk_entry_get_text(GTK_ENTRY(w)); w = glade_xml_get_widget(xmlJP,"entryNome"); string nome = gtk_entry_get_text(GTK_ENTRY(w)); string data_nascimento = formatarData(); string sexo; GtkComboBox *combo = GTK_COMBO_BOX (glade_xml_get_widget(xmlJP,"cbSexo")); int n = gtk_combo_box_get_active (GTK_COMBO_BOX (combo)); if (n==0) sexo = "F"; if (n==1) sexo = "M"; inserirDadosTabelaPaciente(id_interno,nome,data_nascimento,sexo,"N"); inserirDadosTabelaExame(); }//if else { inserirDadosTabelaExame(); tem_cad_paciente=" "; }//else limparCamposCadastro(); }
116
void sair() { gtk_main_quit(); //Encerra o loop principal do GTK } ----------------------Classe MainInterface.h (Aplicação Gerência de Exames)------------------- #include <glade/glade.h> // Carrega os recursos do Glade #include <gtk/gtk.h> //Carrega os recursos do GTK #include <libpq-fe.h> #ifndef unix #include <stdlib.h> #include <string> #include <winsock2.h> #endif #ifdef unix #include <ctype.h> // for isdigit #include <sys/types.h> // for connect(), bind(), accept(), #include <sys/socket.h> // for connect(), listen(), bind() accept(), #include <netinet/in.h> // for ntons(), htonl(), etc... #include <arpa/inet.h> // for inet_addre() #include <unistd.h> // for close(), read(), write() #include <netdb.h> // for gethostbyname #include <sys/time.h> // for timeval struct, and select() #include <string.h> // for memset() #include <map> // for STL map class (Reference counting) #endif //GladeXML * xml; //Cria um ponteiro para uma estrutura GladeXML using std::string; extern "C" { //Fechar aplicação void sair(); //encerra conexão com banco de dados e a aplicação void exit_nicely(); //habilita a aba referente a busca de exames cadastrados void buscarExame(); /*inicializa a janelaConsultaExame para a busca do código referente a um determinado tipo de exame*/ void buscarTipoExame(); //habilita a aba refente a inclusão de um novo exame void novoExame(); //armazena os dados referentes a inclusão de um exame void salvarExame(); //busca os tipos de exames registrados void consultaExame(); //fecha a janela ConsultaExame void fecharJanela(GtkWidget* janela); //faz a conexão com o database void conectarBanco();
117
//retorna resultado da seleção dos tipos de exames PGresult* selectTipoExame(string tipoExame); //armazena o resultado da seleção dos tipoos de exames em uma tabela void colocaDadosTabelaTipoExame(PGresult* res); //Obtem-se o texto armazenado na celula cliclada. void selecionaCodTabelaTipoExame(GtkWidget *clist, gint row, gint column, GdkEventButton *event, gpointer data); /*retorna dados do paciente se o seu id_interno estiver cadastrado, evento referente a saída do campo edit(entryIdInterno)*/ static gboolean buscarDadosPaciente(); /*formata a data de nascimento do paciente para ser armazenada na base de dados*/ string formatarData(); //insere novo paciente em sua base de dados void inserirDadosTabelaPaciente(string id_interno,string nome, string dataNascimento,string sexo, string tem_cadastro); //insere novo exame em sua base de dados void inserirDadosTabelaExame(); //limpar campos referentes a aba de novos registros de exames void limparCamposCadastro(); /*habilita e desabilita botões quando os pages do notebook da janelaPrincipal são selecionadas*/ void alteraEnabledBotoes(GtkNotebook *notebook,GtkNotebookPage *page, gint page_num, gpointer user_data); //retorna os exames cadastrados para um determinado paciente void buscarExamesCadastrados(); /*seta a variavel id_etiqueta_ResulExame com o valor referente ao id_exame da linha selecionada na tabela da janelaResultadoExame*/ void selecionaDadosTabelaExame(GtkWidget *clist, gint row, gint column, GdkEventButton *event, gpointer data); //inicializa a janela referente a Resultado de exames void verResultadoExame(); //armazena os dados referentes a resultado de um determinado exame void salvarResultadoExame(); //encerrar a execução da janelaCOnsultaExame void sairConsultaExame(); /*limpa os edits referentes a um determinado exame(page) na janela ResultadoExame */ void limparCamposResultadoExame(int page); /*seleciona dados(data_coleta, etiqueta_exame e nome_exame) referentes a um determinado exame para que sejam visualizados na janela ResultadoExame*/ void selecionarDadosResultado(); //encerra a execução da janelaResultadoExame void fecharJanelaRE(); /*visualiza os dados na janelaResultadoExame de um exame de hemograma registrado na base de dados*/ void preencherDadosResultadoHemograma(PGresult *res); /*visualiza os dados na janelaResultadoExame de um exame de urina registrado na base de dados*/ void preencherDadosResultadoUrina(PGresult *res); /*visualiza os dados na janelaResultadoExame de um exame de colesterol registrado na base de dados*/ void preencherDadosResultadoColesterol(PGresult *res); //recebe as mensagens que foram enviadas pelo cliente void *receberMensagem(void *s);
118
//aguarda envio de mensagens do cliente void *ouvirCliente(void* i); /*Metodo chamado qdo a aplicação recebe uma mensagem do tipo ADT;verifica se paciente já se encontra registrado na sua base de dados*/ bool pacienteJaTemCadastro(string id_paciente); //retorna o endereço IP da máquina local char *getEndIPLocal(); //efetua update na tabela Paciente void updatePaciente(string id_paciente, string flag); /*gera string contendo resultado de um determinado exame para ser enviado posteriormente ao cliente, este por vez gera a mesma mensagem em HL7 para ser enviada ao servidor que a encaminha ao aplicativo ProntuarioEletronico*/ string criarResultadoParaProntuario(string id_paciente,string flag, string cod_exame); /*gera string contendo busca de um determinado paciente para ser enviado posteriormente ao cliente, este por vez gera a mesma mensagem em HL7 para ser enviada ao servidor que a encaminha ao aplicativo cadastroPaciente*/ string criarBuscaParaCadastroPaciente(string id_paciente); //estabelece conexão com o cliente via socket void enviarParaCliente(string mensagem); /*envia mensagem(string) ao cliente quando o aplicativo estiver rodando em sistema operacional Windows*/ void enviarMensagem(SOCKET s, string msg); /*envia mensagem(string) ao cliente quando o aplicativo estiver rodando em sistema operacional Unix*/ void enviarMensagemUnix(int s, string msg); /*verifica se aplicativo já enviou uma string de busca para o aplicativo Cadastopciente*/ bool jaFezBuscaEmCadastroPaciente(string id_paciente); //armazena dados refentes a busca no aplicativo cadastroPaciente void armazenarNaBusca(string id,string cod_exame); //exclui dados referentes a busca em cadastroPaciente void excluirDaBusca(string id_paciente); //formata data_nascimento recebida de uma mensagem do tipo ADT (inclui "/") string alterarFormatoDataNascimento(string nascimento); } ----------------------------uCliente.pas (Aplicação Cadastro de Pacientes)------------------------- unit uCliente; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, ComCtrls,ImgList, ToolWin, IdComponent,Spin, ExtCtrls, Buttons, IdTCPConnection, IdTCPClient, IdBaseComponent, jpeg, xmldom, XMLIntf, msxmldom, XMLDoc, DB, DBClient, MConnect, SConnect, ScktComp, Mask, DBCtrls, DBTables, UDataModulo, FMTBcd, SqlExpr, Provider, ADODB, Sockets, IdTCPServer; type private public
119
procedure inicialize; procedure RecebeMensagem(msg:String); function PreencheMensagemCadastroPaciente:String; procedure PreencheFormulario; procedure SalvarNoBanco; function IdentificaSexo(i:integer):string; function IdentificaEstadoCivil(i:integer):string; function IdentificaGrupoEtnico(i:integer):string; function IdentificaRaca(i:integer):string; function IdentificaReligiao(i:integer):string; function extrairToken(BaseString, BreakString: string; StringList: TStringList): TStringList; procedure limparCamposRecebimento; procedure salvardadosRecebidos(t: TStringList); procedure fazerConsulta(tabela, campo:string;var nome,nascimento,id,sexo, resultado:string); function preencherMensagemRespostaConsulta(nome,nascimento,sexo,id, resultado,destino, idResposta:string):string; function acrescentarDigito(valor: string):string; end; TClientDataThread = class(TThread) private public ListBuffer :TStringList; TargetList :TStrings; procedure synchAddDataToControl; constructor Create(CreateSuspended: Boolean); procedure Execute; override; procedure Terminate; end; var Form1: TForm1; mensagemEnvio:string; implementation uses IdSocketHandle; // Aqui a classe IdSocketHandle é definida {$R *.dfm} //------------- TClientDataThread impl ------------- constructor TClientDataThread.Create(CreateSuspended: Boolean); begin inherited Create(CreateSuspended); FreeOnTerminate := true; ListBuffer := TStringList.Create; end; procedure TClientDataThread.Terminate; begin ListBuffer.Free; inherited; end;
120
procedure TClientDataThread.Execute; begin Synchronize(synchAddDataToControl); end; procedure TClientDataThread.synchAddDataToControl; begin TargetList.AddStrings(ListBuffer); end; //------------- end TClientDataThread impl -------- procedure TForm1.fazerConsulta(tabela,campo:string; var nome,nascimento,id,sexo, resultado:string); begin if (self.QueryConsulta.Active) then self.queryConsulta.Active:=false; self.QueryConsulta.CommandText:='Select * from paciente where patient_id_internal = '+quotedStr(campo); self.QueryConsulta.Active:=true; if self.QueryConsulta.RecordCount = 0 then resultado:='NF' else resultado:='OK'; nome:= self.queryConsulta.fieldByName('nome_completo').AsString; nascimento:= self.QueryConsulta.fieldByName('data_nascimento').AsString; sexo:= self.QueryConsulta.fieldByName('sexo').AsString; id:= self.QueryConsulta.fieldByName('patient_id_internal').AsString; end; function TForm1.preencherMensagemRespostaConsulta(nome,nascimento,sexo,id, resultado,destino,idResposta:string):string; begin result:= 'CadastroPaciente|'+ 'CadastroPaciente-Resposta|'+ destino+' '+'|'+ idResposta+' '+'|'+ resultado+' '+'|'+ ' '+'|'+ ' '+'|'+ id+' '+'|'+ ' '+'|'+ nome+' '+'|'+ ' '+'|'+ nascimento+' '+'|'+ sexo+' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+
121
' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'+ ' '+'|'; end; procedure TForm1.RecebeMensagem(msg: String); var t: TStringList; msgResposta,nome,sexo,id,nascimento,resultad:string; begin limparCamposRecebimento; t := TStringList.create; t:= extrairToken(msg,'|',t); self.idmensagem.Text:=t[0]; self.tipoMensagem.Text:=t[1]; self.dataMensagem.Text:=t[2]; self.remetente.Text:=t[3]; if (trim(t[1])='ACK')then//se recebeu mensagem de confirmação... begin self.confirmacao.Text:=t[4]; self.idConfirmacao.Text:=t[5]; showmessage('Confirmação recebida!'); end else if (trim(t[1])='RQP') then begin self.fazerConsulta(t[5],t[4],nome,nascimento,id,sexo,resultad); msgResposta:=preencherMensagemRespostaConsulta(nome,nascimento,sexo,id, resultad,t[3],t[6]); if not(ClienteSocket.Active) then ClienteSocket.Active:=true; ClienteSocket.Sendln(msgResposta); ClienteSocket.Active:=false; end; t.Clear; t:= extrairToken(msg,'̈',t); t:= extrairToken(msg,'̈',t); self.MsgsRecebidas.Lines.Append(t[1]); self.MsgsRecebidas.Lines.Append('************************'); end; procedure TForm1.salvarDadosRecebidos(t: TStringList); begin self.ADODataSet1.FieldByName('patient_id_internal').AsString:= t[8]; self.ADODataSet1.FieldByName('nome_completo').AsString:= t[9]; self.ADODataSet1.FieldByName('data_nascimento').AsString:= t[10]; self.ADODataSet1.FieldByName('sexo').AsString:= t[11]; self.ADODataSet1.FieldByName('patient_address').AsString:= t[12];
122
self.ADODataSet1.FieldByName('phone_number_home').AsString:= t[13]; self.ADODataSet1.FieldByName('phone_number_business').AsString:= t[14]; self.ADODataSet1.FieldByName('patient_account_number').AsString:= t[15]; self.ADODataSet1.Post; self.ADODataSet1.Insert; end; function TForm1.extrairToken(BaseString, BreakString: string; StringList: TStringList): TStringList; var EndOfCurrentString: byte; begin repeat EndOfCurrentString := Pos(BreakString, BaseString); if EndOfCurrentString = 0 then StringList.add(BaseString) else StringList.add(Copy(BaseString, 1, EndOfCurrentString - 1)); BaseString := Copy(BaseString, EndOfCurrentString + length(BreakString), length(BaseString) - EndOfCurrentString); until EndOfCurrentString = 0; result := StringList; end; procedure TForm1.sbSalvarClick(Sender: TObject); begin mensagemEnvio:= PreencheMensagemCadastroPaciente; if not(ClienteSocket.Active) then ClienteSocket.Active:=true; ClienteSocket.Sendln(self.PreencheMensagemCadastroPaciente); ClienteSocket.Active:=false; SalvarNoBanco; inicialize; end; function TForm1.PreencheMensagemCadastroPaciente:String; var diaNasc,mesNasc,horaNasc,minNasc,diaM,mesM,horaM,minM: string; begin diaNasc:= self.acrescentarDigito(mesN.Text); mesNasc:= self.acrescentarDigito(mesN.Text); horaNasc:= self.acrescentarDigito(horaN.Text); minNasc:= self.acrescentarDigito(minN.Text); diaM:= self.acrescentarDigito(mesF.Text); mesM:= self.acrescentarDigito(mesF.Text); horaM:= self.acrescentarDigito(horaF.Text); minM:= self.acrescentarDigito(minF.Text); result:= 'CadastroPaciente|'+ 'CadastroPaciente-Inclusao|'+ Set_ID.Text+' '+'|'+ Patient_ID_External.Text+' '+'|'+ Patient_ID_Internal.Text+' '+'|'+ Alternate_Patient_ID.Text+' '+'|'+
123
Sobrenome.Text+'̂'+PrimeiroNome.Text+'̂'+nomeMeio.Text+'̂'+ Sufixo.Text+'̂'+Prefixo.Text+' '+'|'+ Mother_Maiden_Name.Text+' '+'|'+ anoN.Text+mesNasc+diaNasc+horaNasc+minNasc+' '+'|'+ IdentificaSexo(cbSexo.ItemIndex)+' '+'|'+ Patient_Alias.Text+' '+'|'+ cbRaca.Text+' '+'|'+ Patient_Address.Text+' '+'|'+ Phone_Number_Home.Text+' '+'|'+ Phone_Number_Business.Text+' '+'|'+ Primary_Language.Text+' '+'|'+ cbEstadoCivil.Text+' '+'|'+ cbReligiao.Text+' '+'|'+ Patient_Account_Number.Text+' '+'|'+ SSN_Number.Text+' '+'|'+ CNH1.Text+'̂'+CNH2.Text+'̂'+CNH3.Text+' '+'|'+ Mother_Identifier.Text+' '+'|'+ cbGrupoEtnico.Text+' '+'|'+ Birth_Place.Text+' '+'|'+ Multiple_Birth_Indicator.Text+' '+'|'+ Birth_Order.Text+' '+'|'+ Citizenship.Text+' '+'|'+ Veterans_Military_Status.Text+' '+'|'+ Nationality.Text+' '+'|'+ anoF.Text+mesM+diaM+horaM+minM+' '+'|'+ Patient_Death_Indicator.Text+' '+'|'; end; procedure TForm1.FormCreate(Sender: TObject); begin self.ServidorSocket.Active:=true; end; procedure TForm1.ServidorSocketAccept(Sender: TObject; ClientSocket: TCustomIpClient); var msg:string; DataThread: TClientDataThread; begin DataThread:= TClientDataThread.Create(true); msg:= ClientSocket.Receiveln; if msg<>'' then RecebeMensagem(msg); DataThread.Resume; end; end. ----------------------------prontuario.pas (Aplicação Promtuario Eletronico)---------------------- unit prontuario; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, DB, ADODB, DBTables, ScktComp, StdCtrls, ComCtrls, DBCtrls, Menus, Sockets, Grids, DBGrids, IdHL7, IdBaseComponent, IdComponent, IdTCPServer, IdFingerServer, jpeg;
124
type TForm1 = class(TForm) function extrairToken(BaseString, BreakString: string; StringList: TStringList): TStringList; procedure RecebeMensagem(msg: String); procedure salvarNovoCadastroPaciente(t: TStringList); procedure atualizarProntuarioPaciente(t: TStringList); procedure ServidorSocketAccept(Sender: TObject; ClientSocket: TCustomIpClient); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; TClientDataThread = class(TThread) private public ListBuffer :TStringList; TargetList :TStrings; procedure synchAddDataToControl; constructor Create(CreateSuspended: Boolean); procedure Execute; override; procedure Terminate; end; var Form1: TForm1; implementation uses IdSocketHandle; // This is where the IdSocketHandle class is defined. {$R *.dfm} //------------- TClientDataThread impl ------------- constructor TClientDataThread.Create(CreateSuspended: Boolean); begin inherited Create(CreateSuspended); FreeOnTerminate := true; ListBuffer := TStringList.Create; end; procedure TClientDataThread.Terminate; begin ListBuffer.Free; inherited; end; procedure TClientDataThread.Execute; begin Synchronize(synchAddDataToControl); end; procedure TClientDataThread.synchAddDataToControl; begin TargetList.AddStrings(ListBuffer);
125
end; //------------- end TClientDataThread impl ------------- procedure TForm1.RecebeMensagem(msg: String); var t: TStringList; begin t := TStringList.create; t:= extrairToken(msg,'|',t); if (trim(t[1])='ACK')then //se recebeu mensagem de confirmação... trim tira os espaços em branco begin showmessage('Confirmação recebida!'); end else if (trim(t[1])='PIN') then begin atualizarProntuarioPaciente(t); end else if (trim(t[1])='ADT') then salvarNovoCadastroPaciente(t); t.Clear; //t:= extrairToken(msg,'̈',t); end; procedure TForm1.salvarNovoCadastroPaciente(t: TStringList); var cnh : string; begin if (t[22]=' ^ ^ ') then cnh := ''; //inserir na tabela criada self.ADODataSet1.Insert; self.ADODataSet1.FieldByName('patient_id_internal').AsString:= t[4]; self.ADODataSet1.FieldByName('nome_completo').AsString:= t[5]; self.ADODataSet1.FieldByName('data_nascimento').AsString:= t[6]; self.ADODataSet1.FieldByName('sexo').AsString:= t[7]; self.ADODataSet1.FieldByName('patient_address').AsString:= t[8]; self.ADODataSet1.FieldByName('phone_number_home').AsString:= t[9]; self.ADODataSet1.FieldByName('phone_number_business').AsString:= t[10]; self.ADODataSet1.FieldByName('patient_account_number').AsString:= t[11]; self.ADODataSet1.FieldByName('id').AsString:= t[12]; self.ADODataSet1.FieldByName('patient_id_external').AsString:= t[13]; self.ADODataSet1.FieldByName('alternate_patient_id').AsString:= t[14]; self.ADODataSet1.FieldByName('mother_maiden_name').AsString:= t[15]; self.ADODataSet1.FieldByName('patient_alias').AsString:= t[16]; self.ADODataSet1.FieldByName('raca').AsString:= t[17]; self.ADODataSet1.FieldByName('primary_language').AsString:= t[18]; self.ADODataSet1.FieldByName('estado_civil').AsString:= t[19]; self.ADODataSet1.FieldByName('religiao').AsString:= t[20]; self.ADODataSet1.FieldByName('ssn_number').AsString:= t[21]; self.ADODataSet1.FieldByName('cnh').AsString:= cnh; self.ADODataSet1.FieldByName('mother_identifier').AsString:= t[23]; self.ADODataSet1.FieldByName('grupo_etnico').AsString:= t[24]; self.ADODataSet1.FieldByName('birth_place').AsString:= t[25]; self.ADODataSet1.FieldByName('multiple_birth_indicator').AsString:= t[26];
126
self.ADODataSet1.FieldByName('birth_order').AsString:= t[27]; self.ADODataSet1.FieldByName('citizenship').AsString:= t[28]; self.ADODataSet1.FieldByName('veterans_military_status').AsString:= t[29]; self.ADODataSet1.FieldByName('nationality').AsString:= t[30]; self.ADODataSet1.FieldByName('data_falescimento').AsString:= t[31]; self.ADODataSet1.FieldByName('patient_death_indicator').AsString:= t[32]; self.ADODataSet1.Post; end; procedure TForm1.atualizarProntuarioPaciente(t: TStringList); var i,total,exameAtual: integer; begin total:= strToInt(t[7]); exameAtual:= 7; self.ADOBuscaExame.Active:= false; for i:=1 to total do begin inc(exameAtual); self.UpdateSQL1.InsertSQL.Clear; self.UpdateSQL1.InsertSQL.Text:= 'insert into exame (patient_id_internal,dataexame,resultado) values ('+ quotedstr(t[4])+' , '+ quotedstr(t[2])+' , '+ quotedstr(t[exameAtual])+' )'; self.UpdateSQL1.Apply(ukInsert); end; self.ADOBuscaExame.Active:=true; end; function TForm1.extrairToken(BaseString, BreakString: string; StringList: TStringList): TStringList; var EndOfCurrentString: byte; begin repeat EndOfCurrentString := Pos(BreakString, BaseString); if EndOfCurrentString = 0 then StringList.add(BaseString) else StringList.add(Copy(BaseString, 1, EndOfCurrentString - 1)); BaseString := Copy(BaseString, EndOfCurrentString + length(BreakString), length(BaseString) - EndOfCurrentString); until EndOfCurrentString = 0; result := StringList; end; procedure TForm1.ServidorSocketAccept(Sender: TObject; ClientSocket: TCustomIpClient); var msg:string; DataThread: TClientDataThread; begin // create thread DataThread:= TClientDataThread.Create(true); msg:= ClientSocket.Receiveln;
127
if msg<>'' then RecebeMensagem(msg); DataThread.Resume; end; procedure TForm1.FormCreate(Sender: TObject); begin ServidorSocket.Active:=true; end; end. ---------------------------------------cliente.cpp (Aplicação Cliente)--------------------------------- #include "Cliente.h" #include <pthread.h> #include <stdio.h> #ifndef unix #include <io.h> #endif #include <iostream> #include <fstream> #include "CMSH.h" #include "LexicoR.h" #include "SemanticoR.h" #include "SintaticoR.h" #include "AnalysisError.h" #include <exception> #include <iomanip> using namespace std; using std::cout; using std::endl; void Cliente::setAplicacaoOrigem(string aplicOrigem){ aplicacaoOrigem = aplicOrigem; } void Cliente::setTipoMensagem(string tipomsg){ tipoMensagem = tipomsg; } void Cliente::setQueryID(string query){ queryID = query; } void Cliente::setAplicacaoDestino(string aplicDestino){ aplicacaoDestino=aplicDestino; } void Cliente::setQueryResult(string qr){ queryResult = qr; }
128
char* Cliente::getEndIPLocal(){ #ifndef unix WORD wVersionRequested; WSADATA wsaData; PHOSTENT hostinfo; wVersionRequested = MAKEWORD( 1, 1 ); #endif #ifdef unix struct hostent *hostinfo; #endif char name[255]; char *ip; #ifndef unix if ( WSAStartup( wVersionRequested, &wsaData ) == 0 ) #endif if( gethostname ( name, sizeof(name)) == 0) { if((hostinfo = gethostbyname(name)) != NULL) { int nCount = 0; #ifndef unix while(hostinfo->h_addr_list[nCount]){ ip = inet_ntoa(*(struct in_addr *)hostinfo->h_addr_list[nCount]); nCount++; } #endif #ifdef unix ip = inet_ntoa(*((struct in_addr *)hostinfo->h_addr)); #endif } } return ip; } void Cliente::contemArquivoConfigServer(const string& fileName) { FILE *file = fopen(fileName.c_str(), "rb"); if (!file){ string msg = "O arquivo "+fileName+ " nao existe!"; MessageBox(NULL,msg.c_str(),"Cliente HL7",MB_ICONSTOP|MB_OK); exit(1); } else fclose(file); } string Cliente::leiaConfigServidor(){ string serverConfigFile = "configServidor.txt"; contemArquivoConfigServer(serverConfigFile); ifstream serverConfig(serverConfigFile.c_str()); string serverIPAddress; // lê endereço ip do servidor no arquivo txt
129
getline(serverConfig,serverIPAddress); char * c = (char *)serverIPAddress.c_str(); string temp = strtok(c,"\r\n"); serverConfig.close(); serverIPAddress = temp; return serverIPAddress; } #ifndef unix void Cliente::enviarMensagem(SOCKET s, string msg){ int bytesSent; const char* sendbuf = msg.c_str(); bytesSent = send( s, sendbuf, strlen(sendbuf), 0 ); closesocket(s); printf("\nMensagem que foi enviada:"); printf(msg.c_str()); printf("\nBytes: %d",bytesSent); printf("\n"); } #endif #ifdef unix void Cliente::enviarMensagemUnix(int s, string msg){ int bytesSent; const char* sendbuf = msg.c_str(); bytesSent = send( s, sendbuf, strlen(sendbuf), 0 ); close(s); } #endif /*papel cliente qdo quem = "S"(servidor) e papel de servidor qdo quem = "A"(aplicacao)*/ bool Cliente::conectar(string msg, string quem, int porta){ bool conectou = false; #ifndef unix WSADATA wsaData; int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); if ( iResult != NO_ERROR ) printf("Erro no WSAStartup()\n"); SOCKET m_socketEnvio; #endif #ifdef unix int m_socketEnvio; #endif m_socketEnvio = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); #ifndef unix if ( m_socketEnvio == INVALID_SOCKET ) { printf( "Erro no socket %ld\n", WSAGetLastError() ); WSACleanup(); } #endif #ifdef unix if ( m_socketEnvio == -1 ) {
130
} #endif // Connecta com o servidor. sockaddr_in clientService; clientService.sin_family = AF_INET; string ipServidor; if (quem == "S"){//caso a mensagem deva ser enviada ao servidor ipServidor = leiaConfigServidor(); } else{//caso a mensagem deva ser encaminhada para a aplicação ipServidor = getEndIPLocal(); } clientService.sin_port = htons( porta ); clientService.sin_addr.s_addr = inet_addr( ipServidor.c_str()); #ifndef unix if (connect(m_socketEnvio, (SOCKADDR*) &clientService, sizeof(clientService)) == SOCKET_ERROR) { WSACleanup(); string erro="Não foi possível estabelecer conexão com o servidor"+quem; MessageBox(NULL,erro.c_str(),"Cliente HL7",MB_ICONSTOP|MB_OK); } #endif #ifdef unix if (connect(m_socketEnvio, (struct sockaddr*) &clientService, sizeof(clientService) ) == -1) { printf( "Falha na conexao...%s[%d]\n", strerror(errno),errno); }//if #endif else{ #ifndef unix enviarMensagem(m_socketEnvio, msg); #endif #ifdef unix enviarMensagemUnix(m_socketEnvio,msg); #endif conectou = true; }//else // Send data. return conectou; } string Cliente::getHoraAtual(){ time_t hora; struct tm *datetime; char buffer[128]; tzset(); time(&hora); datetime = localtime(&hora); strftime(buffer, sizeof(buffer),"%X", datetime); return (string) buffer; } string Cliente::getDataHoraAtual(){
131
time_t hora; struct tm *datetime; char buffer[128]; tzset(); time(&hora); datetime = localtime(&hora); strftime(buffer, sizeof(buffer),"%Y%m%d %X", datetime); return (string) buffer; } void Cliente::enviarMensagemConfirmacao(CMSH mshR){ string ipLocal = (string)getEndIPLocal(); string cabecalho= "MSH|^~\\&|"+mshR.getSending_Application()+"|"+ipLocal+"|"+ mshR.getSending_Application()+"|"+ // Receiving_Application mshR.getSending_Facility() + "|"+ // Receiving_Facility getDataHoraAtual()+"||ACK^|"+ // Message_Type "IDMSH"+ getDataHoraAtual()+"|"+ // Message_Control_ID "P|2.3<cr>"; string confirmacao = "MSA|AA|"+mshR.getMessage_Control_ID()+"<cr>"; string temp = cabecalho+confirmacao; conectar(temp,"S",27015); } void Cliente::enviarMensagemParaServidorRespostaBuscaPaciente(Cliente *cliente, CPID *cpid){ string ipLocal = (string)cliente->getEndIPLocal(); string cabecalho = "MSH|^~\\&|"+aplicacaoOrigem+"|"+ipLocal+"|"+ aplicacaoDestino+"|id"+ queryID+"|"+getDataHoraAtual()+ "||RPI^A24|IDMSH"+ getDataHoraAtual()+"|P|2.3<cr>"; string confirmacao = "QAK|"+ queryID+"|"+ queryResult+"<cr>"; string temp; if (queryResult!="NF"){ string pid = preenchePID(cpid); temp = cabecalho+confirmacao+pid; }else temp = cabecalho+confirmacao; conectar(temp,"S",27015); } void Cliente::enviarMensagemParaServidorResultadoExame(char *msg, Cliente *c){ string token = strtok(msg,"|"); c->setAplicacaoOrigem(token); token = strtok(NULL,"|"); c->setTipoMensagem(token); token = strtok(NULL,"|"); c->setAplicacaoDestino(token); token = strtok(NULL,"|"); string idPaciente = token; token = strtok(NULL,"|"); string tabela = token; token = strtok(NULL,"|"); int totlinhas = atoi(token.c_str()); string dsp = ""; for (int i=0; i<totlinhas;i++){ token = strtok(NULL,"|");
132
dsp = dsp + "DSP||"+token+"|<cr>"; } string ipLocal = (string)getEndIPLocal(); string msh = "MSH|^~\\&|"+aplicacaoOrigem+"|"+ipLocal+"|"+aplicacaoDestino+ "|IPConfirmacao|"+getDataHoraAtual()+"||PIN^A08|IDMSH"+ getDataHoraAtual()+"|P|2.3<cr>"; string qrd = "QRD|" + getDataHoraAtual()+"|T|I|"+ idPaciente+"|||2000|"+ idPaciente+"|"+ tabela +"|GE||T<cr>"; string temp = msh + qrd+ dsp; conectar(temp,"S",27015); } void Cliente::enviarMensagemParaServidorPedidoBuscaPaciente(char *msg,Cliente *c){ string token = strtok(msg,"|"); c->setAplicacaoOrigem(token); token = strtok(NULL,"|"); c->setTipoMensagem(token); token = strtok(NULL,"|"); c->setAplicacaoDestino(token); token = strtok(NULL,"|"); string idPaciente = token; token = strtok(NULL,"|"); string tabela = token; string ipLocal = (string)getEndIPLocal(); string msh = "MSH|^~\\&|"+aplicacaoOrigem+"|"+ipLocal+"|"+aplicacaoDestino+ "|IPConfirmacao|"+ getDataHoraAtual()+"||RQP^A19|IDMSH"+ getDataHoraAtual()+"|P|2.3<cr>"; string qrd = "QRD|" + getDataHoraAtual()+"|T|I|"+ idPaciente+"|||2000|"+ idPaciente+"|"+ tabela +"|GE||T<cr>"; string qrf = "QRF|" + aplicacaoOrigem+"|||||ANY|ANY|ALL|0800<cr>"; string temp = msh + qrd+ qrf; conectar(temp,"S",27015); } CPID* construirPID(char *msg, Cliente *c){ CPID *cpid = new CPID(); string token = strtok(msg,"|"); c->setAplicacaoOrigem(token); c->setAplicacaoDestino(token); token = strtok(NULL,"|"); c->setTipoMensagem(token); string m = token; if (m == "CadastroPaciente-Resposta"){ token = strtok(NULL,"|"); string z = token; z = token.substr(0,(token.size()-1)); c->setAplicacaoDestino(z); token = strtok(NULL,"|"); string k = token; k = token.substr(0,(token.size()-1));
133
c->setQueryID(k); token = strtok(NULL,"|"); string j = token; j = token.substr(0,(token.size()-1)); c->setQueryResult(j); } token = strtok(NULL,"|"); cpid->setSet_ID(token); token = strtok(NULL,"|"); cpid->setPatient_ID_External(token); token = strtok(NULL,"|"); m = token.substr(0,(token.size()-1)); cpid->setPatient_ID_Internal(m); token = strtok(NULL,"|"); cpid->setAlternate_Patient_ID(token); token = strtok(NULL,"|"); cpid->setPatient_Name(token); token = strtok(NULL,"|"); cpid->setMother_Maiden_Name(token); token = strtok(NULL,"|"); cpid->setDate_Time_of_Birth(token); token = strtok(NULL,"|"); cpid-> setSex(token); token = strtok(NULL,"|"); cpid->setPatient_Alias(token); token = strtok(NULL,"|"); cpid->setRace(token); token = strtok(NULL,"|"); cpid->setPatient_Address(token); token = strtok(NULL,"|"); cpid->setPhone_Number_Home(token); token = strtok(NULL,"|"); cpid->setPhone_Number_Business(token); token = strtok(NULL,"|"); cpid->setPrimary_Language(token); token = strtok(NULL,"|"); cpid->setMarital_Status(token); token = strtok(NULL,"|"); cpid->setReligion(token); token = strtok(NULL,"|"); cpid->setPatient_Account_Number(token); token = strtok(NULL,"|"); cpid->setSSN_Number(token); token = strtok(NULL,"|"); string t = token; if (t==" ") t = " ^ ^ "; cpid->setDriver_License_Number(t); token = strtok(NULL,"|"); cpid->setMother_Identifier(token); token = strtok(NULL,"|"); cpid->setEthnic_Group(token); token = strtok(NULL,"|"); cpid->setBirth_Place(token); token = strtok(NULL,"|"); cpid->setMultiple_Birth_Indicator(token);
134
token = strtok(NULL,"|"); cpid->setBirth_Order(token); token = strtok(NULL,"|"); cpid->setCitizenship(token); token = strtok(NULL,"|"); cpid->setVeterans_Military_Status(token); token = strtok(NULL,"|"); cpid->setNationality(token); token = strtok(NULL,"|"); cpid->setPatient_Death_Date_and_Time(token); token = strtok(NULL,"|"); cpid->setPatient_Death_Indicator(token); return cpid; } string Cliente::construirMensagemAdmissaoParaAplicacao(CMSH msh,CEVN evn, CPID pid, string msg){ return (msh.getMessage_Control_ID()+"|"+ msh.getMessage_Type_Event()+"|"+ msh.getDate_Time_of_Message()+"|"+ msh.getSending_Application()+"|"+ pid.getPatient_ID_Internal()+"|"+pid.getPatient_Name()+"|"+ pid.getDate_Time_of_Birth()+"|"+pid.getSex()+"|"+ pid.getPatient_Address()+"|"+pid.getPhone_Number_Home()+"|"+ pid.getPhone_Number_Business()+"|"+pid.getPatient_Account_Number()+ "|"+ pid.getSet_ID()+"|"+ pid.getPatient_ID_External()+"|"+ pid.getAlternate_Patient_ID()+"|"+ pid.getMother_Maiden_Name()+"|"+ pid.getPatient_Alias()+"|"+ pid.getRace()+"|"+ pid.getPrimary_Language()+"|"+ pid.getMarital_Status()+"|"+ pid.getReligion()+"|"+pid.getSSN_Number()+"|"+ pid.getDriver_License_Number()+"|"+pid.getMother_Identifier()+"|"+ pid.getEthnic_Group()+"|"+ pid.getBirth_Place()+"|"+ pid.getMultiple_Birth_Indicator()+"|"+ pid.getBirth_Order()+"|"+pid.getCitizenship()+"|"+ pid.getVeterans_Military_Status()+"|"+ pid.getNationality()+"|"+ pid.getPatient_Death_Date_and_Time()+"|"+ pid.getPatient_Death_Indicator()+ "|"+"¨"+msg+"¨"); } string Cliente::construirMensagemConfirmacaoParaAplicacao(CMSH msh,CMSA msa, string msg){ return (msh.getMessage_Control_ID()+"|"+ msh.getMessage_Type_Event()+"|"+ msh.getDate_Time_of_Message()+"|"+ msh.getSending_Application()+ +"|"+ msa.getAcknowledgment_Code()+"|"+ msa.getMessage_Control_ID()+"|"+"¨"+msg+"¨"); } string Cliente::construirMensagemParaAplicacaoPedidoDadosPaciente(CMSH msh, CQRD qrd,string msg){ return (msh.getMessage_Control_ID()+"|"+ msh.getMessage_Type_Event()+"|"+ msh.getDate_Time_of_Message()+"|"+ msh.getSending_Application()+"|"+ qrd.getWho_subject_filter()+"|" + qrd.getWhat_subject_filter()+"|"+ qrd.getQuery_ID()+"|"+"¨"+msg+"¨"); } string Cliente::construirMensagemParaAplicacaoRespostaDadosPaciente(CMSH msh,
135
CQAK qak,CPID pid,string msg2){ return (msh.getMessage_Control_ID()+"|"+ msh.getMessage_Type_Event()+"|"+ msh.getDate_Time_of_Message()+"|"+ msh.getSending_Application()+ +"|"+ qak.getQuery_Tag_QAK()+"|"+ qak.getQuery_Response_Status()+"|"+ pid.getPatient_ID_Internal()+"|"+pid.getPatient_Name()+"|"+ pid.getDate_Time_of_Birth()+"|"+pid.getSex()+"|"+ pid.getPatient_Address()+"|"+pid.getPhone_Number_Home()+"|"+ pid.getPhone_Number_Business()+"|"+ pid.getPatient_Account_Number()+"|"+"¨"+msg2+"¨"); } string Cliente::construirMensagemParaAplicacaoRespostaDadosPacienteNF(CMSH msh, CQAK qak,string msg2){ return (msh.getMessage_Control_ID()+"|"+ msh.getMessage_Type_Event()+"|"+ msh.getDate_Time_of_Message()+"|"+ msh.getSending_Application()+ +"|"+ qak.getQuery_Tag_QAK()+"|"+ qak.getQuery_Response_Status()+"|"+ +"¨"+msg2+"¨"); } string Cliente::construirMensagemParaAplicacaoAtualizacaoProntuario(CMSH msh, CQRD qrd,vector<CDSP> vetDSP,string msg){ string tamanho; char buf [3]; string elementoDSP = ""; for (int i=0;i<vetDSP.size();i++) elementoDSP = elementoDSP+ vetDSP[i].getData_Line()+"|"; itoa(vetDSP.size(),buf,10); tamanho = (string)buf; return (msh.getMessage_Control_ID()+"|"+ msh.getMessage_Type_Event()+"|"+ msh.getDate_Time_of_Message()+"|"+ msh.getSending_Application()+ "|"+ qrd.getWho_subject_filter()+"|" + qrd.getWhat_subject_filter()+"|"+ qrd.getQuery_ID()+"|"+tamanho+ "|"+elementoDSP+ "¨"+msg+"¨"); } void* receberMensagem(void *s){ #ifndef unix SOCKET soc = (SOCKET) s; #endif #ifdef unix int soc = (int) s; #endif int bytesRecv = -1; char recvbuf[3000] = ""; bytesRecv = recv( soc, recvbuf, 3000, 0 ); //ver tamanho if (bytesRecv > 0) { try{ Cliente *cliente = new Cliente(); char* msg = recvbuf; printf(msg); string msg2 = msg; /*verifica o tipo de mensagem recebida: se veio do servidor ou da interface */ string msgRecebida = strtok(recvbuf,"|");
136
string origem = msgRecebida; msgRecebida = strtok(NULL,"|"); string tipo = msgRecebida; if (origem =="CadastroPaciente") { CPID *pid = new CPID(); pid = construirPID((char*) msg2.c_str(),cliente); if (tipo =="CadastroPaciente-Inclusao"){ cliente->enviarMensagemCadastroPacienteParaServidor(cliente,pid); }else{ cliente->enviarMensagemParaServidorRespostaBuscaPaciente(cliente,pid); } delete pid; } //if (origem =="CadastroPaciente") else if (origem =="GerenciaExames"){ if (tipo =="ResultadoExame"){ cliente->enviarMensagemParaServidorResultadoExame( (char*) msg2.c_str(),cliente); }else cliente->enviarMensagemParaServidorPedidoBuscaPaciente( (char*) msg2.c_str(),cliente); } else{ //mensagem formato HL7 LexicoR *anLexico = new LexicoR(msg2.c_str()); SintaticoR *anSintatico = new SintaticoR(); SemanticoR *anSemantico = new SemanticoR(); anSintatico->parse(anLexico,anSemantico); CMSH mshR = anSemantico->enviaObjetoMSHR(); string destino = mshR.getReceiving_Application(); int porta = atoi(mshR.getSecurity().c_str()); string msgAplicacao; string evento = mshR.getMessage_Type_Event(); if (evento == "ACK"){ CMSA msaR = anSemantico->enviaObjetoMSAR(); printf("\nRecebeu mensagem de confirmacao!\n"); msgAplicacao = cliente->construirMensagemConfirmacaoParaAplicacao(mshR,msaR,msg2); cliente->conectar(msgAplicacao,"A",porta); }else if (evento == "RQP"){ CQRD qrdR = anSemantico->enviaObjetoQRDR(); printf("\nMensagem de solicitacao de busca recebida!\n"); msgAplicacao = cliente->construirMensagemParaAplicacaoPedidoDadosPaciente(mshR,qrdR,msg2); cliente->conectar(msgAplicacao,"A",porta); }else if (evento == "RPI"){ CQAK qakR = anSemantico->enviaObjetoQAKR(); if (qakR.getQuery_Response_Status()!="NF"){ CPID pidR = anSemantico->enviaObjetoPIDR(); msgAplicacao = cliente->construirMensagemParaAplicacaoRespostaDadosPaciente(mshR,qakR,pidR,msg2); }else msgAplicacao = cliente->construirMensagemParaAplicacaoRespostaDadosPacienteNF(mshR,qakR,msg2); printf("\nRecebeu resposta da solicitacao de busca!\n"); cliente->conectar(msgAplicacao,"A",porta);
137
}else if (evento == "PIN"){ CQRD qrdR = anSemantico->enviaObjetoQRDR(); vector<CDSP> vetDSPR; vetDSPR = anSemantico->enviaVetorDSPR(); printf("\nRecebeu atualizacao prontuario paciente\n"); msgAplicacao = cliente->construirMensagemParaAplicacaoAtualizacaoProntuario(mshR,qrdR,vetDSPR,msg2); cliente->conectar(msgAplicacao,"A",porta); if (cliente->conectar(msgAplicacao,"A",porta)) cliente->enviarMensagemConfirmacao(mshR); }else if (evento == "ADT"){ printf("\nRecebeu mensagem admissao paciente\n"); CEVN evnR = anSemantico->enviaObjetoEVNR(); CPID pidR = anSemantico->enviaObjetoPIDR(); msgAplicacao = cliente->construirMensagemAdmissaoParaAplicacao(mshR, evnR,pidR,msg2); if (cliente->conectar(msgAplicacao,"A",porta)) cliente->enviarMensagemConfirmacao(mshR); } delete anLexico; delete anSintatico; delete anSemantico; }//else mensagem formato HL7 delete cliente; }//try catch (AnalysisError *e){ fprintf(stderr,"Ocorreu exceção: \n", e->getMessage()); }//catch }// if (bytesRecv > 0) } void Cliente::ouvirServidor(){ #ifndef unix // Inicializa Winsock. WSADATA wsaData; //inicia o socket, o MAKEWORD(2,2) encontra versão do winsocket int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); if ( iResult != NO_ERROR ){} // Cria socket. SOCKET m_socket; #endif #ifdef unix int m_socket; #endif m_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); /*primeiro parametro é a familia, segundo é o tipo(TCP ou UDP e o terceiro eh o protocolo associado a familia)*/ #ifndef unix if ( m_socket == INVALID_SOCKET ) { WSACleanup();
138
} #endif #ifdef unix if ( m_socket == -1) { printf("Não foi possível conectar" ); } #endif // Bind the socket. sockaddr_in service; service.sin_family = AF_INET; service.sin_addr.s_addr = inet_addr(getEndIPLocal()); service.sin_port = htons( 27014 ); //bind associa o socket ao endereço #ifndef unix if ( bind( m_socket,(SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR ){ closesocket(m_socket); } #endif #ifdef unix if ( bind( m_socket, (struct sockaddr*)&service, sizeof(service)) == -1 ) { close(m_socket); } #endif //lista o numero max de conexões aceitas pelo servidor if ( listen( m_socket, 10 ) == -1 ){} #ifndef unix SOCKET AcceptSocket; #endif #ifdef unix int AcceptSocket; #endif pthread_t threadCliente; int status; while (true) { //Aguarda por novas conexões AcceptSocket = accept( m_socket, NULL, NULL ); if (AcceptSocket != -1) { int rc = pthread_create(&threadCliente, NULL, receberMensagem, (void *)AcceptSocket); rc = pthread_join(threadCliente, (void **)&status); if (rc){ exit(-1); } #ifndef unix closesocket(AcceptSocket); #endif #ifdef unix close(AcceptSocket); #endif }//fim do if }//fim do while }
139
string Cliente::preenchePID(CPID *cpid){ return("PID|"+cpid->getSet_ID()+"|"+cpid->getPatient_ID_External()+"|"+ cpid->getPatient_ID_Internal()+"|"+cpid->getAlternate_Patient_ID()+"|"+ cpid->getPatient_Name()+"|"+cpid->getMother_Maiden_Name()+"|"+ cpid->getDate_Time_of_Birth()+"|"+cpid->getSex()+"|"+ cpid->getPatient_Alias()+"|"+cpid->getRace()+"|"+ cpid->getPatient_Address()+"||"+cpid->getPhone_Number_Home()+"|"+ cpid->getPhone_Number_Business()+"|"+cpid->getPrimary_Language()+"|"+ cpid->getMarital_Status()+"|"+cpid->getReligion()+"|"+ cpid->getPatient_Account_Number()+"|"+cpid->getSSN_Number()+"|"+ cpid->getDriver_License_Number()+"|"+cpid->getMother_Identifier()+"|"+ cpid->getEthnic_Group()+"|"+cpid->getBirth_Place()+"|"+ cpid->getMultiple_Birth_Indicator()+"|"+cpid->getBirth_Order()+"|"+ cpid->getCitizenship()+"|"+cpid->getVeterans_Military_Status()+"|"+ cpid->getNationality()+"|"+cpid->getPatient_Death_Date_and_Time()+"|"+ cpid->getPatient_Death_Indicator()+"<cr>"); } void Cliente::enviarMensagemCadastroPacienteParaServidor(Cliente *cliente, CPID *cpid){ string ipLocal = (string)getEndIPLocal(); string msh = "MSH|^~\\&|"+aplicacaoOrigem+"|"+ipLocal+"|"+aplicacaoDestino+ "|IPConfirmacao|"+ getDataHoraAtual()+"||ADT^A01|IDMSH"+ getDataHoraAtual()+"|P|2.3<cr>"; string evn = "EVN|A01|"+ getDataHoraAtual()+ "<cr>"; string mensagem = msh + evn + cliente->preenchePID(cpid); cliente->conectar(mensagem,"S",27015); } int main(int argc,char *argv[]){ //O Corpo principal do Programa printf("Cliente inicializado..."); Cliente *cliente = new Cliente(); cliente->setAplicacaoOrigem("CadastroPaciente"); cliente->ouvirServidor(); delete cliente; return 0; } ---------------------------------------cliente.h (Aplicação Cliente)--------------------------------- #ifndef Cliente_H #define Cliente_H #ifndef unix #ifndef WIN32 #define WIN32 #endif #endif #ifndef unix
140
#include <winsock2.h> #include <string> #endif #ifdef unix #include <ctype.h> // for isdigit #include <sys/types.h> // for connect(), bind(), accept(), #include <sys/socket.h> // for connect(), listen(), bind() accept(), #include <netinet/in.h> // for ntons(), htonl(), etc... #include <arpa/inet.h> // for inet_addre() #include <unistd.h> // for close(), read(), write() #include <netdb.h> // for gethostbyname #include <sys/time.h> // for timeval struct, and select() #include <string.h> // for memset() #include <map> // for STL map class (Reference counting) #endif #include "CMSH.h" #include "CPID.h" #include "CEVN.h" #include "CMSA.h" #include "CQRD.h" #include "CQRF.h" #include "CQAK.h" #include "CDSP.h" #include <vector> using std::vector; using std::string; class Cliente { private: string aplicacaoOrigem; string aplicacaoDestino; string tipoMensagem; string queryID; string queryResult; public: void setAplicacaoOrigem(string aplicOrigem); void setAplicacaoDestino(string aplicDestino); void setQueryID(string query); void setTipoMensagem(string tipoMsg); void setQueryResult(string qr); //retorna mensagem de admissão que deverá ser enviada para aplicação string construirMensagemAdmissaoParaAplicacao(CMSH mshR,CEVN evnr, CPID pidr, string msg); //retorna mensagem de confirmação recebida para a aplicação string construirMensagemConfirmacaoParaAplicacao(CMSH msh,CMSA msa, string msg);
141
//retorna mensagem consulta de paciente string construirMensagemParaAplicacaoPedidoDadosPaciente(CMSH msh, CQRD qrd,string msg); //retorna mensagem resultado consulta por paciente localizado string construirMensagemParaAplicacaoRespostaDadosPaciente(CMSH mshR, CQAK qakR,CPID pidR,string msg2); //retorna mensagem para recebimento de resultado(atualizar dados paciente) string construirMensagemParaAplicacaoAtualizacaoProntuario(CMSH msh, CQRD qrd,vector<CDSP> vetDSP,string msg); //retorna mensagem resultado consulta por paciente não encontrado string construirMensagemParaAplicacaoRespostaDadosPacienteNF(CMSH msh, CQAK qak,string msg2); //Retorna o endereço IP local char* getEndIPLocal(); /*Verifica se o arquivo de configuração que contém o end ip do servidor se encontra na pasta do projeto.*/ void contemArquivoConfigServer(const string& fileName); //Retorna o endereço IP do servidor a partir do arquivo configServidor.txt string leiaConfigServidor(); #ifndef unix /*Cliente envia mensagem para o servidor, que por vez o encaminha para a aplicação destino*/ void enviarMensagem(SOCKET s,string msg); #endif #ifdef unix /*Cliente envia mensagem para o servidor, que por vez o encaminha para a aplicação destino*/ void enviarMensagemUnix(int s,string msg); #endif //Cliente conecta com o servidor bool conectar(string mensagem, string quem, int porta); //Envia mensagem de confirmação void enviarMensagemConfirmacao(CMSH mshR); //Envia mensagem HL7 para consulta paciente void enviarMensagemParaServidorPedidoBuscaPaciente(char *msg,Cliente *c); //Envia mensagem HL7 resultante da busca por paciente void enviarMensagemParaServidorRespostaBuscaPaciente(Cliente *cliente, CPID *cpid); //Envia mensagem HL7 para atualização de dados do paciente void enviarMensagemParaServidorResultadoExame(char* msg,Cliente *c);
142
//aguarda novas mensagens enviadas tanto pelo servidor qto pela Aplicação void ouvirServidor(); //envia mensagem HL7 para admissão de paciente void enviarMensagemCadastroPacienteParaServidor(Cliente *cliente, CPID *cpid); //retorna a hora do sistema string getHoraAtual(); //Retorna a data e hora atual string getDataHoraAtual(); //preenche segmemto identificacao paciente string preenchePID(CPID *cpid); }; //Cria thread responsável por aguardar mensagens enviadas pelo servidor extern "C" void *receberMensagem(void *s); #endif ---------------------------------------Servidor.cpp (Aplicação Servidor)----------------------------- #include <pthread.h> #include "LexicoR.h" #include "SemanticoR.h" #include "SintaticoR.h" #include "AnalysisError.h" #include "Server.h" #include <exception> #include <stdio.h> #ifndef unix #include <io.h> #endif #include <iostream> using std::exception; PGconn *conn; pthread_mutex_t mutex; void beginRegion(){ pthread_mutex_lock(&mutex);} void endRegion(){ pthread_mutex_unlock(&mutex); } char* Server::getEndIPLocal(){ #ifndef unix WORD wVersionRequested; WSADATA wsaData; PHOSTENT hostinfo; wVersionRequested = MAKEWORD( 1, 1 ); #endif #ifdef unix
143
struct hostent *hostinfo; #endif char name[255]; char *ip; #ifndef unix if ( WSAStartup( wVersionRequested, &wsaData ) == 0 ) #endif if( gethostname ( name, sizeof(name)) == 0){ if((hostinfo = gethostbyname(name)) != NULL){ int nCount = 0; #ifndef unix while(hostinfo->h_addr_list[nCount]){ ip = inet_ntoa(*(struct in_addr *)hostinfo->h_addr_list[nCount]); nCount++; } #endif #ifdef unix ip = inet_ntoa(*((struct in_addr *)hostinfo->h_addr)); #endif } } return ip; } CMSH Server::validacaoMensagem(char *mensagem){ CMSH mshR; try{ LexicoR *anLexico = new LexicoR(mensagem); SintaticoR *anSintatico = new SintaticoR(); SemanticoR *anSemantico = new SemanticoR(); anSintatico->parse(anLexico,anSemantico); mshR = anSemantico->enviaObjetoMSHR(); string msg = mensagem; printf("Msg recebida: %s\n",msg.c_str()); }//try catch (AnalysisError *e){ printf("Deu exceção: \n", e->getMessage()); } return mshR; } void Server::exit_nicely(){ PQfinish(conn); exit(1); } string Server::getDataHoraAtual(){ time_t hora; struct tm *datetime; char buffer[128]; tzset(); time(&hora); datetime = localtime(&hora);
144
strftime(buffer, sizeof(buffer),"%d/%m/%Y %X", datetime); return (string) buffer; } void Server::inserirErroBanco(string msgErro){ PGresult *res; string sql = "Insert into erros values( '"+msgErro+"', '"+ getDataHoraAtual()+"')"; res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) == PGRES_FATAL_ERROR){ fprintf(stderr, "Erro no banco ao inserir dados em Erros! %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(); } else{ PQclear(res); } } void Server::conectarBanco(){ conn = PQsetdbLogin("localhost","5432",NULL,NULL,"HL7","postgres","phigres*"); if (PQstatus(conn) != CONNECTION_OK){ fprintf(stderr, "Conexao com database falhou: %s", PQerrorMessage(conn)); exit_nicely(); } } bool Server::verificaIPDestino(string ip){ const char *paramValues[1]; PGresult *res; paramValues[0] = ip.c_str(); beginRegion(); res = PQexecParams(conn, "SELECT * FROM Configuracoes WHERE ip = $1",1,NULL, paramValues, NULL,NULL,1); if (PQresultStatus(res) != PGRES_TUPLES_OK){ fprintf(stderr, "Erro durante a seleçao do ip destino: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(); } endRegion(); if (PQntuples(res)>0) return true; else return false; } vector<Aplicacao>Server:: verificarDestino(string aplicacao, string evento, string ip, string aplicOrigem){ PGresult *res; vector<Aplicacao> vetorAplic; /*se tipo de evento for ORU ou ADT, a mensagem deverá ser encaminhada para
145
todas as aplicações registradas na tabela de Configuracao */ if (evento=="ACK"){ const char *paramValues[1]; paramValues[0] = aplicacao.c_str(); beginRegion(); res = PQexecParams(conn,"SELECT * FROM Configuracoes WHERE aplicacao = $1", 1, NULL, paramValues, NULL, NULL, 1); if (PQresultStatus(res) != PGRES_TUPLES_OK){ fprintf(stderr, "Erro ao selecionar dados da Aplicacao: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }else{ int ip_fnum = PQfnumber(res, "IP"); int porta_fnum = PQfnumber(res, "porta"); int aplic_fnum = PQfnumber(res, "aplicacao"); char *ip_ptr, *porta_ptr, *aplic_ptr; for (int i=0 ; i<PQntuples(res);i++){ ip_ptr = PQgetvalue(res, i, ip_fnum); porta_ptr = PQgetvalue(res, i, porta_fnum); aplic_ptr = PQgetvalue(res, i, aplic_fnum); Aplicacao aplic; aplic.endIP = (string)ip_ptr; aplic.porta = (string)porta_ptr; vetorAplic.push_back(aplic); }//for }//else endRegion(); } else{ if ((evento== "RQP")||(evento=="RPI")||(evento=="PIN")){ const char *paramValues[1]; paramValues[0] = aplicacao.c_str(); string sql= "Select * from Configuracoes where aplicacao = $1"; beginRegion(); res = PQexecParams(conn, sql.c_str(),1,NULL,paramValues,NULL,NULL,1); if (PQresultStatus(res) != PGRES_TUPLES_OK){ fprintf(stderr,"Erro ao selecionar todos os dados em Configuracoes!%s", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if else { int ip_fnum = PQfnumber(res, "IP"); int porta_fnum = PQfnumber(res, "porta"); int aplic_fnum = PQfnumber(res, "aplicacao"); char *ip_ptr, *porta_ptr, *aplic_ptr; for (int i=0 ; i<PQntuples(res);i++){ ip_ptr = PQgetvalue(res, i, ip_fnum); porta_ptr = PQgetvalue(res, i, porta_fnum); aplic_ptr = PQgetvalue(res, i, aplic_fnum); Aplicacao aplic; aplic.endIP = (string)ip_ptr; aplic.porta = (string)porta_ptr; vetorAplic.push_back(aplic); }//for
146
}//else } else{ const char *paramValues[2]; paramValues[0] = ip.c_str(); paramValues[1] = aplicOrigem.c_str(); string sql= "Select * from Configuracoes where (ip <> $1 or aplicacao <> $2) and aplicacao <> $2"; beginRegion(); res = PQexecParams(conn, sql.c_str(),2,NULL,paramValues,NULL,NULL,1); if (PQresultStatus(res) != PGRES_TUPLES_OK){ fprintf(stderr,"Erro ao selecionar todos os dados em Configuracoes!%s", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if else { int ip_fnum = PQfnumber(res, "IP"); int porta_fnum = PQfnumber(res, "porta"); int aplic_fnum = PQfnumber(res, "aplicacao"); char *ip_ptr, *porta_ptr, *aplic_ptr; for (int i=0 ; i<PQntuples(res);i++){ ip_ptr = PQgetvalue(res, i, ip_fnum); porta_ptr = PQgetvalue(res, i, porta_fnum); aplic_ptr = PQgetvalue(res, i, aplic_fnum); Aplicacao aplic; aplic.endIP = (string)ip_ptr; aplic.porta = (string)porta_ptr; vetorAplic.push_back(aplic); }//for }//else }//else }endRegion(); PQclear(res); return vetorAplic; } void Server::armazenarCaixaSaida(string ipDestino, string ipRemetente, string msg, string porta, string idMsg){ PGresult *res; string dataHora = getDataHoraAtual(); string sql = "Insert into caixasaida values( '"+ipDestino+"', '"+ipRemetente+ "', "+porta+", '"+msg+"', '"+idMsg+"', to_timestamp('"+dataHora+ "','DD/MM/YYYY HH24:MI:SS'))"; beginRegion(); res = PQexec(conn, sql.c_str()); if (PQresultStatus(res) == PGRES_FATAL_ERROR){ fprintf(stderr, "Erro no banco ao inserir dados na Caixa Saida! %s", PQerrorMessage(conn)); PQclear(res); inserirErroBanco("Erro no banco ao inserir dados na Caixa Saida: "+sql); exit_nicely(); } else{ PQclear(res);
147
printf("Dados armazenados na caixa de saida!\n"); } endRegion(); } void Server::enviarMensagem(string msg, vector<Aplicacao> ips, CMSH cmshr){ int bytesSent; string ipRemetente = cmshr.getSending_Facility(); string msgAlterada; int total = ips.size(); printf("\nTotal IPs encontrados: %d",total); for (int i=0; i<ips.size();i++){ string porta = ips[i].porta; msgAlterada = adicionarPortaNaMsgRecebida(msg, porta); #ifndef unix SOCKET socDestino; #endif #ifdef unix int socDestino; #endif socDestino = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); #ifndef unix if ( socDestino == INVALID_SOCKET ) { printf( "Erro no socket(): %ld\n", WSAGetLastError() ); } #endif #ifdef unix if ( socDestino == -1 ) { printf( "Erro no socket() \n"); } #endif else{ sockaddr_in service; string ip = ips[i].endIP; service.sin_family = AF_INET; service.sin_addr.s_addr = inet_addr( ip.c_str() ); service.sin_port = htons( atoi("27014") ); #ifndef unix if (connect( socDestino, (SOCKADDR*) &service, sizeof(service))== SOCKET_ERROR) { printf("Nao foi possivel conectar com o destino %s\n", ip.c_str()); char * c = (char *)msgAlterada.c_str(); string tok = strtok(c,"\\"); string temp = tok+ "\\\\&"; temp = temp + strtok(NULL,"&" ); armazenarCaixaSaida(ips[i].endIP, ipRemetente, temp, ips[i].porta, cmshr.getMessage_Control_ID());
148
}//if #endif #ifdef unix if (connect( socDestino, (struct sockaddr*)&service, sizeof(service)) == -1) { printf("Nao foi possivel conectar com o destino %s\n", ip.c_str()); char * c = (char *)msgAlterada.c_str(); string tok = strtok(c,"\\"); string temp = tok+ "\\\\&"; temp = temp + strtok(NULL,"&" ); armazenarCaixaSaida(ips[i].endIP, ipRemetente,temp,ips[i].porta, cmshr.getMessage_Control_ID()); }//if #endif else{ const char* sendbuf = msgAlterada.c_str(); bytesSent = send( socDestino, sendbuf, strlen(sendbuf), 0 ); printf("Mensagem enviada ao destino %s\n",ip.c_str()); printf("Porta: %s\n",porta.c_str()); printf( "_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n" ); //ips.erase(ips.begin()+i); }//else }//else #ifndef unix closesocket(socDestino); #endif #ifdef unix close(socDestino); #endif }//for } void Server::excluiCaixaMensagem(char* ip, char* idmsg, char* porta){ const char *paramValues[3]; PGresult *res; paramValues[0] = ip; paramValues[2] = idmsg; paramValues[1] = porta; res = PQexecParams(conn,"DELETE FROM CaixaSaida WHERE \"ipDestino\" = $1 and porta = $2 and \"idMsg\" = $3", 3, NULL,paramValues,NULL,NULL,1); if (PQresultStatus(res) == PGRES_FATAL_ERROR){ fprintf(stderr, "Erro no banco ao excluir dados na Caixa de Saida! %s", PQerrorMessage(conn)); PQclear(res); inserirErroBanco("Erro no banco ao excluir msg "+(string)idmsg+ " na Caixa de Saida"); exit_nicely(); } else{ PQclear(res); printf("Msg excluida da caixa de saida!\n");
149
} } PGresult* Server::temMsgCaixaSaida(){ PGresult *res; bool temMsgExpirada = false; string temp = "SELECT \"ipDestino\", \"ipOrigem\", porta, mensagem, \"idMsg\", now()-\"dataHora\" as tempocaixa FROM CaixaSaida"; beginRegion(); res = PQexec(conn,temp.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK){ fprintf(stderr,"Falha ao selecionar dados da caixa de saida: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if else{ if (PQntuples(res)>0){ int dataHora_fnum = PQfnumber(res, "tempocaixa"); int ipDestino_fnum = PQfnumber(res, "\"ipDestino\""); int idmsg_fnum = PQfnumber(res, "\"idMsg\""); int porta_fnum = PQfnumber(res, "porta"); int msg_fnum = PQfnumber(res, "mensagem"); char *ip_ptr, *porta_ptr, *idmsg_ptr, *msg_ptr, *dataHora_ptr; for (int i = 0; i < PQntuples(res);i++){ dataHora_ptr = PQgetvalue(res, i, dataHora_fnum); if (atoi(dataHora_ptr) > 4){ ip_ptr = PQgetvalue(res, i, ipDestino_fnum); idmsg_ptr = PQgetvalue(res, i, idmsg_fnum); porta_ptr = PQgetvalue(res, i, porta_fnum); msg_ptr = PQgetvalue(res, i, msg_fnum); excluiCaixaMensagem(ip_ptr, idmsg_ptr, porta_ptr); inserirErroBanco("Expirou msg caixa saida!Destino: "+(string)ip_ptr+ " porta: "+(string)porta_ptr+"/MSG: "+(string)msg_ptr); temMsgExpirada = true; printf("Tem msg expirada...\n"); }//if }//for }//if endRegion(); }//else if (temMsgExpirada){ PQclear(res); beginRegion();//mutex res = PQexec(conn, "SELECT * FROM CaixaSaida"); if (PQresultStatus(res) != PGRES_TUPLES_OK){ fprintf(stderr,"Falha refresh caixa de saida: %s",PQerrorMessage(conn)); PQclear(res); exit_nicely(); }//if endRegion(); }//if return res; } void Server::envioMensagensCaixaSaida(PGresult *res){
150
int ipDestino_fnum = PQfnumber(res, "\"ipDestino\""); int idmsg_fnum = PQfnumber(res, "\"idMsg\""); int porta_fnum = PQfnumber(res, "porta"); int msg_fnum = PQfnumber(res, "mensagem"); char *ip_ptr, *porta_ptr, *idmsg_ptr, *msg_ptr; for (int i = 0; i < PQntuples(res);i++){ ip_ptr = PQgetvalue(res, i, ipDestino_fnum); idmsg_ptr = PQgetvalue(res, i, idmsg_fnum); porta_ptr = PQgetvalue(res, i, porta_fnum); msg_ptr = PQgetvalue(res, i, msg_fnum); if (verificaIPDestino((string)ip_ptr)){ #ifndef unix SOCKET socDestino; #endif #ifdef unix int socDestino; #endif socDestino = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); #ifndef unix if ( socDestino == INVALID_SOCKET ) { printf( "Erro no socket(): %ld\n", WSAGetLastError() ); WSACleanup(); } #endif #ifdef unix if ( socDestino == -1) { printf( "Erro no socket\n" ); } #endif sockaddr_in service; service.sin_family = AF_INET; service.sin_addr.s_addr = inet_addr( ip_ptr ); service.sin_port = htons( 27014 ); #ifndef unix if ( connect( socDestino, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR){ printf("Nao foi possivel conectar com o destino %s",ip_ptr); printf(" da msg da cxa saida.\n"); }//if #endif #ifdef unix if (connect(socDestino,(struct sockaddr*)&service, sizeof(service)) == -1){ printf("Nao foi possivel conectar com o destino %s",ip_ptr); printf(" da msg da cxa saida.\n"); }//if #endif else{ const char* sendbuf = msg_ptr; int bytesSent = send( socDestino, sendbuf, strlen(sendbuf), 0);
151
printf( "Mensagem enviada ao destino com sucesso!! \n"); printf( "_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n\n" ); excluiCaixaMensagem(ip_ptr, idmsg_ptr, porta_ptr); #ifndef unix closesocket(socDestino); #endif #ifdef unix close(socDestino); #endif }//else }//if }//for } #ifndef unix void Server::enviarMensagemErro(SOCKET s,string msg, CMSH cmshr){ string ipOrigem = cmshr.getSending_Facility(); sockaddr_in service; service.sin_family = AF_INET; service.sin_addr.s_addr = inet_addr(ipOrigem.c_str()); service.sin_port = htons( 27014 ); if (connect( s, (SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR){ printf("Msg de erro enviado para caixa de Saida \n"); armazenarCaixaSaida(ipOrigem,"Servidor", msg, "27014", cmshr.getMessage_Control_ID()); } else{ printf("Msg de erro enviado para %s\n",ipOrigem.c_str()); const char* sendbuf = msg.c_str(); int bytesSent = send( s, sendbuf, strlen(sendbuf), 0 ); if (bytesSent < 0){ armazenarCaixaSaida(ipOrigem,"Servidor", msg, "27014", cmshr.getMessage_Control_ID()); } closesocket(s); }//else } #endif #ifdef unix void Server::enviarMensagemErroUnix(int soc,string msg, CMSH cmshr){ string ipOrigem = cmshr.getSending_Facility(); sockaddr_in service; service.sin_family = AF_INET; service.sin_addr.s_addr = inet_addr(ipOrigem.c_str()); service.sin_port = htons( 27014 ); if (connect( soc,(struct sockaddr*) &service, sizeof(service)) == -1){ printf("Msg de erro enviado para caixa de Saida \n"); armazenarCaixaSaida(ipOrigem,"Servidor", msg, "27014", cmshr.getMessage_Control_ID()); } else{ printf("Msg de erro enviado para %s\n",ipOrigem.c_str()); const char* sendbuf = msg.c_str(); int bytesSent = send( soc, sendbuf, strlen(sendbuf), 0 );
152
if (bytesSent < 0){ armazenarCaixaSaida(ipOrigem,"Servidor", msg, "27014", cmshr.getMessage_Control_ID()); } close(soc); }//else } #endif string Server::adicionarPortaNaMsgRecebida(string msg, string porta){ string mensagem = msg; int i = mensagem.find("||",0); mensagem.replace(i+1,0,porta); printf("\nPorta acrescentada na mensagem"); printf(mensagem.c_str()); printf("\n"); return mensagem; } void *receberMensagem(void * s){ #ifndef unix SOCKET soc = (SOCKET) s; #endif #ifdef unix int soc = (int) s; #endif Server *servidor = new Server(); int bytesRecv = -1; char recvbuf[3000] = ""; vector<Aplicacao> vetorAplic; bytesRecv = recv( soc, recvbuf, 3000, 0 ); if (bytesRecv > 0) { CMSH cmshr = servidor->validacaoMensagem(recvbuf); string aplicdestino = cmshr.getReceiving_Application(); string evento = cmshr.getMessage_Type_Event(); if (aplicdestino!= "" ){ vetorAplic = servidor->verificarDestino(aplicdestino, evento, cmshr.getSending_Facility(), cmshr.getSending_Application()); if (vetorAplic.size()>0){ printf("Mensagem recebida de %s\n",cmshr.getSending_Facility().c_str()); printf("Mensagem encaminhada para %s\n",aplicdestino.c_str()); printf( "_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n\n" ); servidor->enviarMensagem((string)recvbuf,vetorAplic,cmshr); } else{ printf("Destino da msg recebida nao foi encontrado!\n"); #ifndef unix servidor->enviarMensagemErro(soc,"Aplicacao "+aplicdestino+ " inexistente./nConsulte o administrador do sistema.", cmshr); #endif #ifdef unix servidor->enviarMensagemErroUnix(soc,"Aplicacao "+aplicdestino+ " inexistente./nConsulte o administrador do sistema.", cmshr); #endif
153
}//else } //if }//if delete servidor; } #ifndef unix SOCKET Server::inicializar(){ // Inicializa Winsock. WSADATA wsaData; //inicia o socket, o MAKEWORD(2,2) encontra versão do winsocket int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); if ( iResult != NO_ERROR ) printf("Erro no WSAStartup()\n"); // Create a socket. SOCKET m_socket; m_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); /*primeiro parametro é a familia, segundo é o tipo(TCP ou UDP e o terceiro eh o protocolo //associado a familia)*/ if ( m_socket == INVALID_SOCKET ) { printf( "Erro no socket(): %ld\n", WSAGetLastError() ); WSACleanup(); } sockaddr_in service; service.sin_family = AF_INET; service.sin_addr.s_addr = inet_addr(getEndIPLocal()); service.sin_port = htons( 27015 ); //bind associa o socket ao endereço if ( bind( m_socket,(SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR){ printf( "bind falhou.\n" ); closesocket(m_socket); } //lista o numero max de conexões aceitas pelo servidor if ( listen( m_socket, 1 ) == SOCKET_ERROR ) printf( "Erro listening do socket.\n"); return m_socket; } #endif #ifdef unix int Server::inicializarUnix(){ int m_socket; m_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); //primeiro parametro é a familia, segundo é o tipo(TCP ou UDP) e o terceiro // é o protocolo associado a familia) if ( m_socket == -1 ) { printf( "Erro no socket \n"); } sockaddr_in service; service.sin_family = AF_INET;
154
service.sin_addr.s_addr = inet_addr(getEndIPLocal()); service.sin_port = htons( 27015 ); //bind associa o socket ao endereço if ( bind( m_socket, (struct sockaddr*) &service, sizeof(service)) == -1 ) { printf( "bind falhou.\n" ); close(m_socket); } //lista o numero max de conexões aceitas pelo servidor if ( listen( m_socket, 1 ) == -1 ) printf( "Erro listening do socket.\n"); return m_socket; } #endif void* controleCaixaDeSaida(void * i){ PGresult *res; Server *servidor = new Server(); time_t horainicial, horaatual, hora; time(&horainicial); hora = horainicial; time(&horaatual); //verifica caixa de saida se já se passaram 10 min da ultima verificação if (hora <= horaatual){ res = servidor->temMsgCaixaSaida(); if (PQntuples(res)>0){ printf("Envio msgs caixa de saida...\n"); servidor->envioMensagensCaixaSaida(res); //intervalo de tempo para verficar a caixa de saida. hora = horainicial+360; }//if }//if delete servidor; return 0; } void* ouvirConexoes(void * soc){ #ifndef unix SOCKET AcceptSocket; SOCKET m_socket = (SOCKET) soc; #endif #ifdef unix int AcceptSocket; int m_socket = (int) soc; #endif pthread_t threadCliente; int status; printf( "Aguardando conexao...\n" ); printf( "_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n\n" ); //aguarda novas conexões AcceptSocket = accept( m_socket, NULL, NULL ); if (AcceptSocket != -1) {
155
int rc = pthread_create(&threadCliente, NULL, receberMensagem,(void *)AcceptSocket) ; rc = pthread_join(threadCliente, (void **)&status); if (rc){ printf("ERROR; return code from pthread_create de receberMensagem() %d\n", rc); exit(-1); }//if #ifndef unix closesocket(AcceptSocket); #endif #ifdef unix close(AcceptSocket); #endif }//if return 0; } int main(int argc,char *argv[]) { Server *servidor = new Server(); servidor->conectarBanco(); #ifndef unix SOCKET soc = servidor->inicializar(); #endif #ifdef unix int soc = servidor->inicializarUnix(); #endif //Criação do mutex if (pthread_mutex_init(&mutex, NULL) < 0) { perror("pthread_mutex_init"); exit(1); } pthread_attr_t attr; pthread_t threads[2]; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); while(true){ int rc1 = pthread_create(&threads[0],&attr,controleCaixaDeSaida,(void *)1); if (rc1){ printf("ERRO pthread_create() controleCaixaDeSaida eh %d\n", rc1); exit(-1); }//if int rc2 = pthread_create(&threads[1], &attr, ouvirConexoes, (void *)soc) ; if (rc2){ printf("ERRO pthread_create() ouvirConexoes eh %d\n", rc2); exit(-1); }//if for (int i = 0; i <2; i++) { pthread_join(threads[i], NULL); } }//while delete servidor; return 0; }
156
---------------------------------------Servidor.h (Aplicação Servidor)----------------------------- #ifndef Server_H #define Server_H #ifndef unix #ifndef WIN32 #define WIN32 #endif #endif #ifndef unix #include <winsock2.h> #include <stdlib.h> #include <string> #include <time.h> #endif #include <libpq-fe.h> #ifdef unix #include <ctype.h> // for isdigit #include <sys/types.h> // for connect(), bind(), accept(), #include <sys/socket.h> // for connect(), listen(), bind() accept(), #include <netinet/in.h> // for ntons(), htonl(), etc... #include <arpa/inet.h> // for inet_addre() #include <unistd.h> // for close(), read(), write() #include <netdb.h> // for gethostbyname #include <sys/time.h> // for timeval struct, and select() #include <string.h> // for memset() #include <map> // for STL map class (Reference counting) #endif #include <vector> #include "CMSH.h" using std::vector; using std::string; typedef struct Aplicacao{ string endIP; string porta; string aplicacaoNome; }Aplicacao; class Server{ public: //Retorna o IP da máquina local char* getEndIPLocal(); //verifica a porta da aplicação destino e adiciona na mensagem
157
string adicionarPortaNaMsgRecebida(string msg, string porta); //Método para verificar a consistência da mensagem HL7 CMSH validacaoMensagem(char *mensagem); //Encerra conexão com o banco void exit_nicely(); //Busca data e hora atual retornando um string no formato da data do postgres(YYMMDD) string getDataHoraAtual(); //Insere um registro na tabela Erros quando ocorrer erros: exclusão, qdo expirar mensagem da caixa de saída void inserirErroBanco(string msgErro); //realiza a conexão com o banco de dados postgres void conectarBanco(); //Verifica se o IP do destino da mensagem da caixa de saída é válido(encontra-se na tabela de configurações bool verificaIPDestino(string ip); //Retorna os IPs correspondentes a determinadas aplicações vector<Aplicacao> verificarDestino(string aplicacao, string evento, string ip, string aplicOrigem); //Insere valores referentes a mensagem na caixa de saída quando o destino não estiver on-line void armazenarCaixaSaida(string ipDestino, string ipRemetente, string msg, string porta, string idMsg); //Envia mensagem para o(s) IP(s) destino void enviarMensagem(string msg, vector<Aplicacao> ips, CMSH cmshr); //Ao enviar uma mensagem da caixa de saída exclui da tabela caixaSaida void excluiCaixaMensagem(char* ip, char* idmsg, char* porta); //Verifica se existem mensagens para serem enviadas na caixa de saída PGresult* temMsgCaixaSaida(); //Envia mensagem da caixaSaida void envioMensagensCaixaSaida(PGresult *res); //Envia mensagem de erro para o cliente que enviou uma mensagem com destino inválido #ifndef unix void enviarMensagemErro(SOCKET s,string msg, CMSH cmshr); #endif #ifdef unix void enviarMensagemErroUnix(int s,string msg, CMSH cmshr); #endif //Inicializa o socket #ifndef unix SOCKET inicializar(); #endif #ifdef unix int inicializarUnix(); #endif
158
}; //Thread para aceitar novas conexões com o servidor extern "C" void *ouvirConexoes(void * soc); //Thread para tentar enviar mensagens da caixa de saída após determinado tempo extern "C" void *controleCaixaDeSaida(void * i); //thread que recebe as mensagens enviadas através do socket extern "C" void *receberMensagem(void * s); #endif ---------------------------------------AnalysisError.h (Usado nas aplicações Servidor e Cliente)----------------------------- #ifndef ANALYSIS_ERROR_H #define ANALYSIS_ERROR_H #include <string> class AnalysisError { public: AnalysisError(const std::string &msg, int position = -1) : message(msg), position(position) { } const char *getMessage() const { return message.c_str(); } int getPosition() const { return position; } private: std::string message; int position; }; ---------------------------------------CDSP.cpp (Usado nas aplicações Servidor e Cliente)-------------------------- #include "CDSP.h" CDSP::CDSP(){ Segment_ID_DSP = ""; Display_Level =""; Data_Line=""; Logical_Break_Point=""; Result_ID=""; Fim_Linha=""; } void CDSP::setSegment_ID_DSP(string token){ Segment_ID_DSP = token; } string CDSP::getSegment_ID_DSP(){ return Segment_ID_DSP; }
159
void CDSP::setDisplay_Level(string token){ Display_Level = token; } string CDSP::getDisplay_Level(){ return Display_Level; } void CDSP::setData_Line(string token){ Data_Line = token; } string CDSP::getData_Line(){ return Data_Line; } void CDSP::setLogical_Break_Point(string token){ Logical_Break_Point = token; } string CDSP::getLogical_Break_Point(){ return Logical_Break_Point; } void CDSP::setResult_ID(string token){ Result_ID = token; } string CDSP::getResult_ID(){ return Result_ID; } void CDSP::setFim_Linha(string token){ Fim_Linha = token; } string CDSP::getFim_Linha(){ return Fim_Linha; } string CDSP::getDSP(){ return (Segment_ID_DSP + Display_Level+ Data_Line+ Logical_Break_Point+ Result_ID+ Fim_Linha); } ----------------------------------CEVN.cpp (Usado nos Aplicações Cliente e Servidor)----------------------------- include "CEVN.h" CEVN::CEVN(){ Segment_ID = ""; Event_Type_Code = ""; Recorded_Date_Time = ""; Date_Time_Planned_Event = ""; Event_Reason_Code = ""; Operator_ID = ""; Event_Occurred = "";
160
Fim_Linha = ""; } void CEVN::setSegment_ID(string token){ Segment_ID = token; } string CEVN::getSegment_ID(){ return Segment_ID; } void CEVN::setEvent_Type_Code (string token){ Event_Type_Code = token; } string CEVN::getEvent_Type_Code(){ return Event_Type_Code; } void CEVN::setRecorded_Date_Time (string token){ Recorded_Date_Time = token; } string CEVN::getRecorded_Date_Time(){ return Recorded_Date_Time; } void CEVN::setDate_Time_Planned_Event(string token){ Date_Time_Planned_Event = token; } string CEVN::getDate_Time_Planned_Event (){ return Date_Time_Planned_Event; } void CEVN::setEvent_Reason_Code(string token){ Event_Reason_Code = token; } string CEVN::getEvent_Reason_Code (){ return Event_Reason_Code; } void CEVN::setOperator_ID(string token){ Operator_ID = token; } string CEVN::getOperator_ID (){ return Operator_ID; }
161
void CEVN::setEvent_Occurred(string token){ Event_Occurred= token; } string CEVN::getEvent_Occurred (){ return Event_Occurred; } void CEVN::setFim_Linha(string token){ Fim_Linha = token; } string CEVN::getFim_Linha(){ return Fim_Linha; } string CEVN::getEVN(){ return (Segment_ID+ Event_Type_Code+Recorded_Date_Time +Date_Time_Planned_Event + Event_Reason_Code +Operator_ID + Event_Occurred + Fim_Linha); } ----------------------------------CMSA.cpp (Usado nos aplicações Cliente e Servidoror)--------------------------- #include "CMSA.h" CMSA::CMSA(){ Segment_ID = ""; Acknowledgment_Code= ""; Message_Control_ID = ""; Text_Message = ""; Expected_Sequence_Number= ""; Delayed_Acknowledgment_Type = ""; Error_Condition = ""; Fim_Linha= ""; } void CMSA::setSegment_ID (string token){ Segment_ID = token; } string CMSA::getSegment_ID(){ return Segment_ID; } void CMSA::setAcknowledgment_Code (string token){ Acknowledgment_Code = token; } string CMSA::getAcknowledgment_Code(){ return Acknowledgment_Code; } void CMSA::setMessage_Control_ID (string token){ Message_Control_ID = token;
162
} string CMSA::getMessage_Control_ID(){ return Message_Control_ID; } void CMSA::setText_Message (string token){ Text_Message = token; } string CMSA::getText_Message(){ return Text_Message; } void CMSA::setExpected_Sequence_Number (string token){ Expected_Sequence_Number = token; } string CMSA::getExpected_Sequence_Number(){ return Expected_Sequence_Number; } void CMSA::setDelayed_Acknowledgment_Type (string token){ Delayed_Acknowledgment_Type =token; } string CMSA::getDelayed_Acknowledgment_Type(){ return Delayed_Acknowledgment_Type ; } void CMSA::setError_Condition (string token){ Error_Condition = token; } string CMSA::getError_Condition(){ return Error_Condition; } void CMSA::setFim_Linha (string token){ Fim_Linha = token; } string CMSA::getFim_Linha(){ return Fim_Linha; } string CMSA::getMSA(){ return (Segment_ID + Acknowledgment_Code+ Message_Control_ID + Text_Message+ Expected_Sequence_Number + Delayed_Acknowledgment_Type +Error_Condition +Fim_Linha); } -----------------------------------CMSH.cpp (Usado nas aplicações Cliente e Servidor)----------------------------- #include "CMSH.h" CMSH::CMSH(){ Segment_ID = ""; Field_Separator = ""; Encoding_Caracters = ""; Sending_Application = ""; Sending_Facility = ""; Receiving_Application = ""; Receiving_Facility = "";
163
Date_Time_of_Message = ""; Security = ""; Message_Type_Event = ""; Message_Type_Segment = ""; Message_Control_ID = ""; Processing_ID = ""; Version_ID = ""; Fim_Linha = ""; } string CMSH::getSegment_ID(){ return Segment_ID; } void CMSH::setSegment_ID (string token){ Segment_ID = token; } void CMSH::setField_Separator(string token){ Field_Separator = token; } string CMSH::getField_Separator(){ return Field_Separator; } void CMSH::setEncoding_Caracters(string token){ Encoding_Caracters = token; } string CMSH::getEncoding_Caracters(){ return Encoding_Caracters; } void CMSH::setSending_Application(string token){ Sending_Application = token; } string CMSH::getSending_Application(){ return Sending_Application; } void CMSH::setSending_Facility (string token){ Sending_Facility = token; } string CMSH::getSending_Facility(){ return Sending_Facility; } void CMSH::setReceiving_Application (string token){ Receiving_Application = token; } string CMSH::getReceiving_Application (){ return Receiving_Application; } void CMSH::setReceiving_Facility (string token){ Receiving_Facility = token; } string CMSH::getReceiving_Facility (){ return Receiving_Facility; } void CMSH::setDate_Time_of_Message (string token){ Date_Time_of_Message = token; }
164
string CMSH::getDate_Time_of_Message(){ return Date_Time_of_Message; } void CMSH::setSecurity (string token){ Security = token; } string CMSH::getSecurity(){ return Security; } string CMSH::getMessage_Type(){ return (Message_Type_Event + " "+Message_Type_Segment); } void CMSH::setMessage_Type_Event (string token){ Message_Type_Event = token; } string CMSH::getMessage_Type_Event(){ return Message_Type_Event; } void CMSH::setMessage_Type_Segment (string token){ Message_Type_Segment = token; } string CMSH::getMessage_Type_Segment(){ return Message_Type_Segment; } void CMSH::setMessage_Control_ID (string token){ Message_Control_ID = token; } string CMSH::getMessage_Control_ID(){ return Message_Control_ID; } void CMSH::setProcessing_ID (string token){ Processing_ID = token; } string CMSH::getProcessing_ID(){ return Processing_ID; } void CMSH::setVersion_ID (string token){ Version_ID = token; } string CMSH::getVersion_ID(){ return Version_ID; } void CMSH::setFim_Linha(string token){ Fim_Linha = token; } string CMSH::getFim_Linha(){ return Fim_Linha; } string CMSH::getMSH(){ return (Segment_ID+Field_Separator+Encoding_Caracters+Sending_Application+ Sending_Facility+Receiving_Application+ Receiving_Facility+ Date_Time_of_Message+ Security+ Message_Type_Event+ Message_Type_Segment+ Message_Control_ID + Processing_ID + Version_ID + Fim_Linha); } ------------------------------------CPID.cpp (Usado nas aplicações Cliente e Servidor)-----------------------------
165
#include "CPID.h" CPID::CPID(){ Segment_ID = ""; Set_ID = ""; Patient_ID_External = ""; Patient_ID_Internal = ""; Alternate_Patient_ID = ""; Patient_Name = ""; Mother_Maiden_Name = ""; Date_Time_of_Birth = ""; Sex = ""; Patient_Alias = ""; Race = ""; Patient_Address = ""; County_Code = ""; Phone_Number_Home = ""; Phone_Number_Business = ""; Primary_Language = ""; Marital_Status = ""; Religion = ""; Patient_Account_Number = ""; SSN_Number = ""; Driver_License_Number = ""; Mother_Identifier = ""; Ethnic_Group = ""; Birth_Place = ""; Multiple_Birth_Indicator = ""; Birth_Order = ""; Citizenship = ""; Veterans_Military_Status = ""; Nationality = ""; Patient_Death_Date_and_Time = ""; Patient_Death_Indicator =""; } void CPID:: setSegment_ID (string token){ Segment_ID = token; } string CPID:: getSegment_ID(){ return Segment_ID; } void CPID:: setSet_ID (string token){ Set_ID = token; } string CPID:: getSet_ID (){ return Set_ID; } void CPID:: setPatient_ID_External (string token){ Patient_ID_External = token; } string CPID:: getPatient_ID_External(){ return Patient_ID_External; }
166
void CPID:: setPatient_ID_Internal (string token){ Patient_ID_Internal= token; } string CPID:: getPatient_ID_Internal(){ return Patient_ID_Internal; } void CPID:: setAlternate_Patient_ID (string token){ Alternate_Patient_ID = token; } string CPID:: getAlternate_Patient_ID(){ return Alternate_Patient_ID; } void CPID:: setPatient_Name (string token){ Patient_Name = token; } string CPID:: getPatient_Name(){ return Patient_Name; } void CPID:: setMother_Maiden_Name (string token){ Mother_Maiden_Name= token; } string CPID:: getMother_Maiden_Name(){ return Mother_Maiden_Name; } void CPID:: setDate_Time_of_Birth (string token){ Date_Time_of_Birth = token; } string CPID:: getDate_Time_of_Birth(){ return Date_Time_of_Birth; } void CPID:: setSex (string token){ Sex= token; } string CPID:: getSex(){ return Sex; } void CPID:: setPatient_Alias (string token){ Patient_Alias= token; } string CPID:: getPatient_Alias(){ return Patient_Alias; } void CPID:: setRace (string token){ Race = token; } string CPID:: getRace(){ return Race; } void CPID:: setPatient_Address (string token){ Patient_Address= token; } string CPID:: getPatient_Address(){ return Patient_Address; }
167
void CPID:: setCounty_Code (string token){ County_Code= token; } string CPID:: getCounty_Code(){ return County_Code; } void CPID:: setPhone_Number_Home (string token){ Phone_Number_Home = token; } string CPID:: getPhone_Number_Home(){ return Phone_Number_Home; } void CPID:: setPhone_Number_Business (string token){ Phone_Number_Business = token; } string CPID:: getPhone_Number_Business(){ return Phone_Number_Business; } void CPID:: setPrimary_Language (string token){ Primary_Language = token; } string CPID:: getPrimary_Language(){ return Primary_Language; } void CPID:: setMarital_Status (string token){ Marital_Status = token; } string CPID:: getMarital_Status(){ return Marital_Status ; } void CPID:: setReligion (string token){ Religion = token; } string CPID:: getReligion(){ return Religion; } void CPID:: setPatient_Account_Number (string token){ Patient_Account_Number = token; } string CPID:: getPatient_Account_Number(){ return Patient_Account_Number ; } void CPID:: setSSN_Number (string token){ SSN_Number= token; } string CPID:: getSSN_Number(){ return SSN_Number; } void CPID:: setDriver_License_Number (string token){ Driver_License_Number = token; } string CPID:: getDriver_License_Number(){ return Driver_License_Number ; }
168
void CPID:: setMother_Identifier (string token){ Mother_Identifier= token; } string CPID:: getMother_Identifier(){ return Mother_Identifier; } void CPID:: setEthnic_Group (string token){ Ethnic_Group = token; } string CPID:: getEthnic_Group(){ return Ethnic_Group ; } void CPID:: setBirth_Place (string token){ Birth_Place = token; } string CPID:: getBirth_Place(){ return Birth_Place ; } void CPID:: setMultiple_Birth_Indicator (string token){ Multiple_Birth_Indicator = token; } string CPID:: getMultiple_Birth_Indicator(){ return Multiple_Birth_Indicator; } void CPID:: setBirth_Order (string token){ Birth_Order = token; } string CPID:: getBirth_Order(){ return Birth_Order ; } void CPID:: setCitizenship (string token){ Citizenship= token; } string CPID:: getCitizenship(){ return Citizenship; } void CPID:: setVeterans_Military_Status (string token){ Veterans_Military_Status = token; } string CPID:: getVeterans_Military_Status(){ return Veterans_Military_Status ; } void CPID:: setNationality (string token){ Nationality = token; } string CPID:: getNationality(){ return Nationality ; } void CPID:: setPatient_Death_Date_and_Time (string token){ Patient_Death_Date_and_Time = token; } string CPID:: getPatient_Death_Date_and_Time(){ return Patient_Death_Date_and_Time ; } void CPID:: setPatient_Death_Indicator (string token){ Patient_Death_Indicator = token; }
169
string CPID:: getPatient_Death_Indicator(){ return Patient_Death_Indicator; } void CPID:: setFim_Linha (string token){ Fim_Linha = token; } string CPID:: getFim_Linha(){ return Fim_Linha; } string CPID:: getPID(){ return (Segment_ID+ Set_ID+ Patient_ID_External+ Patient_ID_Internal+ Alternate_Patient_ID+ Patient_Name+ Mother_Maiden_Name+ Date_Time_of_Birth+ Sex+ Patient_Alias+ Race+ Patient_Address+ County_Code+Phone_Number_Home+ Phone_Number_Business+ Primary_Language+ Marital_Status+ Religion+ Patient_Account_Number+ SSN_Number+Driver_License_Number+Mother_Identifier+ Ethnic_Group+ Birth_Place+ Multiple_Birth_Indicator+ Birth_Order+Citizenship+ Veterans_Military_Status+Nationality+Patient_Death_Date_and_Time+Patient_Death_Indicator+Fim_Linha); } -----------------------------------CQAK.cpp (Usado nas aplicações Cliente e Servidor)----------------------------- #include "CQAK.h" CQAK::CQAK(){ Segment_ID_QAK = ""; Query_Tag_QAK = ""; Query_Response_Status = ""; Fim_Linha = ""; } void CQAK::setSegment_ID_QAK(string token){ Segment_ID_QAK = token; } string CQAK::getSegment_ID_QAK(){ return Segment_ID_QAK; } void CQAK::setQuery_Tag_QAK(string token){ Query_Tag_QAK = token; } string CQAK::getQuery_Tag_QAK(){ return Query_Tag_QAK; } void CQAK::setQuery_Response_Status(string token){ Query_Response_Status = token; } string CQAK::getQuery_Response_Status(){ return Query_Response_Status; } void CQAK::setFim_Linha(string token){ Fim_Linha = token; } string CQAK::getFim_Linha(){ return Fim_Linha; }
170
string CQAK::getQAK(){ return (Segment_ID_QAK +Query_Tag_QAK+ Query_Response_Status+ Fim_Linha); } ----------------------------------CQRD.cpp (Usado nas aplicações Cliente e Servidor)----------------------------- #include "CQRD.h" CQRD::CQRD(){ Segment_ID = ""; Query_date_time = ""; Query_format_code= ""; Query_priority= ""; Query_ID= ""; Deferred_response_type= ""; Deferred_response_date_time= ""; Quantity_limited_request= ""; Who_subject_filter= ""; What_subject_filter= ""; What_department_data_code= ""; What_data_code_value_qualifier= ""; Query_results_level= ""; Fim_Linha = ""; } void CQRD::setSegment_ID(string token){ Segment_ID = token; } string CQRD::getSegment_ID(){ return Segment_ID; } void CQRD::setQuery_date_time(string token){ Query_date_time = token; } string CQRD::getQuery_date_time(){ return Query_date_time; } void CQRD::setQuery_format_code(string token){ Query_format_code = token; } string CQRD::getQuery_format_code(){ return Query_format_code; } void CQRD::setQuery_priority(string token){ Query_priority = token;} string CQRD::getQuery_priority(){ return Query_priority; } void CQRD::setQuery_ID(string token){ Query_ID= token; } string CQRD::getQuery_ID(){ return Query_ID; } void CQRD::setDeferred_response_type(string token){ Deferred_response_type= token; }
171
string CQRD::getDeferred_response_type(){ return Deferred_response_type; } void CQRD::setDeferred_response_date_time(string token){ Deferred_response_date_time= token; } string CQRD::getDeferred_response_date_time(){ return Deferred_response_date_time; } void CQRD::setQuantity_limited_request(string token){ Quantity_limited_request= token; } string CQRD::getQuantity_limited_request(){ return Quantity_limited_request; } void CQRD::setWho_subject_filter(string token){ Who_subject_filter= token; } string CQRD::getWho_subject_filter(){ return Who_subject_filter; } void CQRD::setWhat_subject_filter(string token){ What_subject_filter= token; } string CQRD::getWhat_subject_filter(){ return What_subject_filter; } void CQRD::setWhat_department_data_code(string token){ What_department_data_code= token; } string CQRD::getWhat_department_data_code(){ return What_department_data_code; } void CQRD::setWhat_data_code_value_qualifier(string token){ What_data_code_value_qualifier = token; } string CQRD::getWhat_data_code_value_qualifier(){ return What_data_code_value_qualifier; } void CQRD::setQuery_results_level(string token){ Query_results_level= token; } string CQRD::getQuery_results_level(){ return Query_results_level;} void CQRD::setFim_Linha(string token){ Fim_Linha = token; } string CQRD::getFim_Linha(){ return Fim_Linha; } string CQRD::getQRD(){ return (Segment_ID +Query_date_time+ Query_format_code+ Query_priority +Query_ID + Deferred_response_type+ Deferred_response_date_time+Quantity_limited_request+ Who_subject_filter+ What_subject_filter+ What_department_data_code+ What_data_code_value_qualifier+ Query_results_level+Fim_Linha); }
172
----------------------------------CQRF.cpp (Usado nas aplicações Servidor e Cliente)----------------------------- #include "CQRF.h" CQRF::CQRF(){ Segment_ID = ""; Where_Subject_Filter = ""; When_Data_Start_Date_Time = ""; When_Data_End_Date_Time = ""; What_User_Qualifier = ""; Other_QRY_Subject_Filter = ""; Which_Date_Time_Qualifier = ""; Which_Date_Time_Status_Qualifier = ""; Date_Time_Selection_Qualifier = ""; When_Quantity_Timing_Qualifier = ""; Fim_Linha = ""; } void CQRF::setSegment_ID(string token){ Segment_ID = token; } string CQRF::getSegment_ID(){ return Segment_ID ; } void CQRF::setWhere_Subject_Filter(string token){ Where_Subject_Filter = token; } string CQRF::getWhere_Subject_Filter(){ return Where_Subject_Filter; } void CQRF::setWhen_Data_Start_Date_Time(string token){ When_Data_Start_Date_Time= token; } string CQRF::getWhen_Data_Start_Date_Time(){ return When_Data_Start_Date_Time; } void CQRF::setWhen_Data_End_Date_Time(string token){ When_Data_End_Date_Time = token; } string CQRF::getWhen_Data_End_Date_Time(){ return When_Data_End_Date_Time; } void CQRF::setWhat_User_Qualifier(string token){ What_User_Qualifier= token; } string CQRF::getWhat_User_Qualifier(){ return What_User_Qualifier; } void CQRF::setOther_QRY_Subject_Filter(string token){ Other_QRY_Subject_Filter= token; } string CQRF::getOther_QRY_Subject_Filter(){ return Other_QRY_Subject_Filter; }
173
void CQRF::setWhich_Date_Time_Qualifier(string token){ Which_Date_Time_Qualifier= token; } string CQRF::getWhich_Date_Time_Qualifier(){ return Which_Date_Time_Qualifier; } void CQRF::setWhich_Date_Time_Status_Qualifier(string token){ Which_Date_Time_Status_Qualifier= token; } string CQRF::getWhich_Date_Time_Status_Qualifier(){ return Which_Date_Time_Status_Qualifier; } void CQRF::setDate_Time_Selection_Qualifier(string token){ Date_Time_Selection_Qualifier= token; } string CQRF::getDate_Time_Selection_Qualifier(){ return Date_Time_Selection_Qualifier; } void CQRF::setWhen_Quantity_Timing_Qualifier(string token){ When_Quantity_Timing_Qualifier= token; } string CQRF::getWhen_Quantity_Timing_Qualifier(){ return When_Quantity_Timing_Qualifier; } void CQRF::setFim_Linha(string token){ Fim_Linha = token; } string CQRF::getFim_Linha(){ return Fim_Linha; } string CQRF::getQRF(){ return (Where_Subject_Filter+ When_Data_Start_Date_Time + When_Data_End_Date_Time+ What_User_Qualifier + Other_QRY_Subject_Filter+ Which_Date_Time_Qualifier+ Which_Date_Time_Status_Qualifier+ Date_Time_Selection_Qualifier+ When_Quantity_Timing_Qualifier+ Fim_Linha); } ----------------------------------Lexico.cpp (Usado nas aplicações Servidor e Cliente)----------------------------- #include "Lexico.h" void Lexico::setInput(const char *input) { this->input = input; setPosition(0); } Token *Lexico::nextToken() throw (LexicalError) {
174
if ( ! hasInput() ) return 0; unsigned start = position; int state = 0; int oldState = 0; int endState = -1; int end = -1; while (hasInput()) { oldState = state; state = nextState(nextChar(), state); if (state < 0) break; else { if (tokenForState(state) >= 0) { endState = state; end = position; } } } if (endState < 0 || (endState != state && tokenForState(oldState) == -2)) throw LexicalError(SCANNER_ERROR[oldState], start); position = end; TokenId token = tokenForState(endState); if (token == 0) return nextToken(); else { std::string lexeme = input.substr(start, end-start); token = lookupToken(token, lexeme); return new Token(token, lexeme, start); } } int Lexico::nextState(unsigned char c, int state) const { int next = SCANNER_TABLE[state][c]; return next; } TokenId Lexico::tokenForState(int state) const { int token = -1; if (state >= 0 && state < STATES_COUNT) token = TOKEN_STATE[state];
175
return static_cast<TokenId>(token); } TokenId Lexico::lookupToken(TokenId base, const std::string &key) { int start = SPECIAL_CASES_INDEXES[base]; int end = SPECIAL_CASES_INDEXES[base+1]-1; while (start <= end) { int half = (start+end)/2; const std::string current = SPECIAL_CASES_KEYS[half]; if (current == key) return static_cast<TokenId>(SPECIAL_CASES_VALUES[half]); else if (current < key) start = half+1; else //(current > key) end = half-1; } return base; } -------------------------------Sintatico.cpp (Usado nas aplicações Servidor e Cliente)----------------------------- #include "Sintatico.h" void Sintatico::parse(Lexico *scanner, Semantico *semanticAnalyser) throw (AnalysisError) { this->scanner = scanner; this->semanticAnalyser = semanticAnalyser; //Limpa a pilha while (! stack.empty()) stack.pop(); stack.push(DOLLAR); stack.push(START_SYMBOL); if (previousToken != 0 && previousToken != currentToken) delete previousToken; previousToken = 0; if (currentToken != 0) delete currentToken; currentToken = scanner->nextToken(); while ( ! step() ) ; } bool Sintatico::step() throw (AnalysisError) { if (currentToken == 0) //Fim de Sentenca { int pos = 0; if (previousToken != 0) pos = previousToken->getPosition() + previousToken->getLexeme().size();
176
currentToken = new Token(DOLLAR, "$", pos); } int a = currentToken->getId(); int x = stack.top(); stack.pop(); if (x == EPSILON) { return false; } else if (isTerminal(x)) { if (x == a) { if (stack.empty()) return true; else { if (previousToken != 0) delete previousToken; previousToken = currentToken; currentToken = scanner->nextToken(); return false; } } else { throw SyntaticError(PARSER_ERROR[x], currentToken->getPosition()); } } else if (isNonTerminal(x)) { if (pushProduction(x, a)) return false; else throw SyntaticError(PARSER_ERROR[x], currentToken->getPosition()); } else // isSemanticAction(x) { semanticAnalyser->executeAction(x-FIRST_SEMANTIC_ACTION, previousToken); return false; } } bool Sintatico::pushProduction(int topStack, int tokenInput) { int p = PARSER_TABLE[topStack-FIRST_NON_TERMINAL][tokenInput-1]; if (p >= 0) { int *production = PRODUCTIONS[p]; //empilha a produção em ordem reversa int length = production[0]; for (int i=length; i>=1; i--)
177
{ stack.push( production[i] ); } return true; } else return false; } ------------------------------SemanticError.h (Usado nas aplicações Servidor e Cliente)------------------------- #ifndef SEMANTIC_ERROR_H #define SEMANTIC_ERROR_H #include "AnalysisError.h" #include <string> class SemanticError : public AnalysisError { public: SemanticError(const std::string &msg, int position = -1) : AnalysisError(msg, position) { } }; #endif -------------------------------SyntaticError.h (Usado nas aplicações Servidor e Cliente)-------------------------- #ifndef SYNTATIC_ERROR_H #define SYNTATIC_ERROR_H #include "AnalysisError.h" #include <string> class SyntaticError : public AnalysisError { public: SyntaticError(const std::string &msg, int position = -1) : AnalysisError(msg, position) { } };#endif -----------------------------------Token.h (Usado nas aplicações Servidor e Cliente)----------------------------- #ifndef TOKEN_H #define TOKEN_H #include "Constants.h" #include <string> class Token {
178
public: Token(TokenId id, const std::string &lexeme, int position) : id(id), lexeme(lexeme), position(position) { } TokenId getId() const { return id; } const std::string &getLexeme() const { return lexeme; } int getPosition() const { return position; } private: TokenId id; std::string lexeme; int position; }; #endif -----------------------------Semantico.cpp (Usado nas aplicações Servidor e Cliente)----------------------------- #include "Semantico.h" void Semantico::executeAction(int action, const Token *token) throw (SemanticError ){ switch (action){ case 01: executeAction01(token); break; case 02: executeAction02(token); break; case 03: executeAction03(token); break; case 04: executeAction04(token); break; case 05: executeAction05(token); break; case 06: executeAction06(token); break; case 07: executeAction07(token); break; case 8: executeAction08(token); break; case 9: executeAction09(token); break; case 10: executeAction10(token); break; case 1010: executeAction1010(token); break; case 11: executeAction11(token); break; case 12: executeAction12(token); break; case 13: executeAction13(token); break; case 14: executeAction14(token); break; case 15: executeAction15(token); break; case 16: executeAction16(token); break; case 17: executeAction17(token); break; case 18: executeAction18(token); break; case 19: executeAction19(token); break; case 20: executeAction20(token); break; case 21: executeAction21(token); break; case 22: executeAction22(token); break; case 23: executeAction23(token); break; case 24: executeAction24(token); break; case 25: executeAction25(token); break; case 26: executeAction26(token); break; case 27: executeAction27(token); break; case 28: executeAction28(token); break; case 29: executeAction29(token); break; case 30: executeAction30(token); break; case 31: executeAction31(token); break; case 32: executeAction32(token); break; case 33: executeAction33(token); break;
case 34: executeAction34(token); break; case 35: executeAction35(token); break; case 36: executeAction36(token); break; case 37: executeAction37(token); break; case 38: executeAction38(token); break; case 39: executeAction39(token); break; case 40: executeAction40(token); break; case 41: executeAction41(token); break; case 42: executeAction42(token); break; case 43: executeAction43(token); break; case 44: executeAction44(token); break; case 45: executeAction45(token); break; case 46: executeAction46(token); break; case 47: executeAction47(token); break; case 48: executeAction48(token); break; case 49: executeAction49(token); break; case 50: executeAction50(token); break; case 51: executeAction51(token); break; case 52: executeAction52(token); break; case 53: executeAction53(token); break; case 54: executeAction54(token); break; case 55: executeAction55(token); break; case 56: executeAction56(token); break; case 57: executeAction57(token); break; case 58: executeAction58(token); break; case 59: executeAction59(token); break; case 60: executeAction60(token); break; case 61: executeAction61(token); break; case 62: executeAction62(token); break; case 63: executeAction63(token); break; case 114: executeAction114(token); break; case 115: executeAction115(token); break; case 116: executeAction116(token); break; case 117: executeAction117(token); break;
179
case 118: executeAction118(token); break; case 119: executeAction119(token); break; case 120: executeAction120(token); break; case 121: executeAction121(token); break; case 122: executeAction122(token); break; case 123: executeAction123(token); break; case 124: executeAction124(token); break; case 125: executeAction125(token); break; case 126: executeAction126(token); break; case 127: executeAction127(token); break; case 128: executeAction128(token); break; case 129: executeAction129(token); break; case 130: executeAction130(token); break; case 131: executeAction131(token); break; case 132: executeAction132(token); break; case 133: executeAction133(token); break; case 134: executeAction134(token); break; case 135: executeAction135(token); break; case 136: executeAction136(token); break; case 137: executeAction137(token); break; case 138: executeAction138(token); break; case 160: executeAction160(token); break; case 161: executeAction161(token); break; case 162: executeAction162(token); break; case 163: executeAction163(token); break; case 164: executeAction164(token); break; case 165: executeAction165(token); break; case 174: executeAction174(token); break; case 175: executeAction175(token); break; case 176: executeAction176(token); break; case 1760: executeAction176(token); break;
180
} } void Semantico::executeAction01(const Token *token) throw (SemanticError ){ msh = new CMSH(); msh->setSegment_ID(token->getLexeme()); } void Semantico::executeAction02(const Token *token) throw (SemanticError ){ msh->setField_Separator(token->getLexeme()); } void Semantico::executeAction03(const Token *token) throw (SemanticError ){ msh->setEncoding_Caracters(msh->getEncoding_Caracters()+token->getLexeme()); } void Semantico::executeAction04(const Token *token) throw (SemanticError ){ msh->setSending_Application(msh->getSending_Application()+token->getLexeme()); } void Semantico::executeAction05(const Token *token) throw (SemanticError ){ msh->setSending_Facility(msh->getSending_Facility()+token->getLexeme()); } void Semantico::executeAction06(const Token *token) throw (SemanticError ){ msh->setReceiving_Application(msh->getReceiving_Application()+ token->getLexeme()); } void Semantico::executeAction07(const Token *token) throw (SemanticError ){ msh->setReceiving_Facility(msh->getReceiving_Facility()+ token->getLexeme()); } void Semantico::executeAction08(const Token *token) throw (SemanticError ){ msh->setDate_Time_of_Message(msh->getDate_Time_of_Message()+token->getLexeme()); } void Semantico::executeAction09(const Token *token) throw (SemanticError ){ msh->setSecurity(msh->getSecurity()+token->getLexeme()); } void Semantico::executeAction10(const Token *token) throw (SemanticError ){ msh->setMessage_Type_Event(msh->getMessage_Type_Event()+token->getLexeme()); } void Semantico::executeAction1010(const Token *token) throw (SemanticError ){ msh->setMessage_Type_Segment(msh->getMessage_Type_Segment()+token->getLexeme()); } void Semantico::executeAction11(const Token *token) throw (SemanticError ){ msh->setMessage_Control_ID(msh->getMessage_Control_ID()+token->getLexeme()); } void Semantico::executeAction12(const Token *token) throw (SemanticError ){ msh->setProcessing_ID(msh->getProcessing_ID()+token->getLexeme()); } void Semantico::executeAction13(const Token *token) throw (SemanticError ){ msh->setVersion_ID(token->getLexeme()); }
181
void Semantico::executeAction14(const Token *token) throw (SemanticError ){ msh->setFim_Linha(token->getLexeme()); } void Semantico::executeAction15(const Token *token) throw (SemanticError ){ if (pid== NULL ){ pid = new CPID(); } pid->setSegment_ID(pid->getSegment_ID()+token->getLexeme()); } void Semantico::executeAction16(const Token *token) throw (SemanticError ){ pid->setSet_ID(pid->getSet_ID()+token->getLexeme()); } void Semantico::executeAction17(const Token *token) throw (SemanticError ){ pid->setPatient_ID_External(pid->getPatient_ID_External()+token->getLexeme()); } void Semantico::executeAction18(const Token *token) throw (SemanticError ){ pid->setPatient_ID_Internal(pid->getPatient_ID_Internal()+token->getLexeme()); } void Semantico::executeAction19(const Token *token) throw (SemanticError ){ pid->setAlternate_Patient_ID(pid->getAlternate_Patient_ID()+token->getLexeme()); } void Semantico::executeAction20(const Token *token) throw (SemanticError ){ pid->setPatient_Name(pid->getPatient_Name()+token->getLexeme()); } void Semantico::executeAction21(const Token *token) throw (SemanticError ){ pid->setMother_Maiden_Name(pid->getMother_Maiden_Name()+token->getLexeme()); } void Semantico::executeAction22(const Token *token) throw (SemanticError ){ pid->setDate_Time_of_Birth(pid->getDate_Time_of_Birth()+token->getLexeme()); } void Semantico::executeAction23(const Token *token) throw (SemanticError ){ pid->setSex(pid->getSex()+token->getLexeme()); } void Semantico::executeAction24(const Token *token) throw (SemanticError ){ pid->setPatient_Alias(pid->getPatient_Alias() + token->getLexeme()); } void Semantico::executeAction25(const Token *token) throw (SemanticError ){ pid->setRace(pid->getRace()+token->getLexeme()); } void Semantico::executeAction26(const Token *token) throw (SemanticError ){ pid->setPatient_Address(pid->getPatient_Address()+token->getLexeme()); } void Semantico::executeAction27(const Token *token) throw (SemanticError ){ pid->setCounty_Code(pid->getCounty_Code()+token->getLexeme()); }
182
void Semantico::executeAction28(const Token *token) throw (SemanticError ){ pid->setPhone_Number_Home(pid->getPhone_Number_Home()+token->getLexeme()); } void Semantico::executeAction29(const Token *token) throw (SemanticError ){ pid->setPhone_Number_Business(pid->getPhone_Number_Business()+token->getLexeme()); } void Semantico::executeAction30(const Token *token) throw (SemanticError ){ pid->setPrimary_Language(pid->getPrimary_Language()+token->getLexeme()); } void Semantico::executeAction31(const Token *token) throw (SemanticError ){ pid->setMarital_Status(pid->getMarital_Status()+token->getLexeme()); } void Semantico::executeAction32(const Token *token) throw (SemanticError ){ pid->setReligion(pid->getReligion()+token->getLexeme()); } void Semantico::executeAction33(const Token *token) throw (SemanticError ){ pid->setPatient_Account_Number(pid->getPatient_Account_Number()+token->getLexeme()); } void Semantico::executeAction34(const Token *token) throw (SemanticError ){ pid->setSSN_Number(pid->getSSN_Number()+token->getLexeme()); } void Semantico::executeAction35(const Token *token) throw (SemanticError ){ pid->setDriver_License_Number(pid->getDriver_License_Number()+token->getLexeme()); } void Semantico::executeAction36(const Token *token) throw (SemanticError ){ pid->setMother_Identifier(pid->getMother_Identifier()+token->getLexeme()); } void Semantico::executeAction37(const Token *token) throw (SemanticError ){ pid->setEthnic_Group(pid->getEthnic_Group()+token->getLexeme()); } void Semantico::executeAction38(const Token *token) throw (SemanticError ){ pid->setBirth_Place(pid->getBirth_Place()+token->getLexeme()); } void Semantico::executeAction39(const Token *token) throw (SemanticError ){ pid->setMultiple_Birth_Indicator(pid->getMultiple_Birth_Indicator()+token->getLexeme()); } void Semantico::executeAction40(const Token *token) throw (SemanticError ){ pid->setBirth_Order(pid->getBirth_Order()+token->getLexeme()); } void Semantico::executeAction41(const Token *token) throw (SemanticError ){ pid->setCitizenship(pid->getCitizenship()+token->getLexeme()); } void Semantico::executeAction42(const Token *token) throw (SemanticError ){
183
pid->setVeterans_Military_Status(pid->getVeterans_Military_Status()+token->getLexeme()); } void Semantico::executeAction43(const Token *token) throw (SemanticError ){ pid->setNationality(pid->getNationality()+token->getLexeme()); } void Semantico::executeAction44(const Token *token) throw (SemanticError ){ pid->setPatient_Death_Date_and_Time(pid->getPatient_Death_Date_and_Time()+token->getLexeme()); } void Semantico::executeAction45(const Token *token) throw (SemanticError ){ pid->setPatient_Death_Indicator(pid->getPatient_Death_Indicator()+token->getLexeme()); } void Semantico::executeAction46(const Token *token) throw (SemanticError ){ pid->setFim_Linha(token->getLexeme()); } void Semantico::executeAction47(const Token *token) throw (SemanticError ){ evn = new CEVN(); evn->setSegment_ID(evn->getSegment_ID()+token->getLexeme()); } void Semantico::executeAction48(const Token *token) throw (SemanticError ){ evn->setEvent_Type_Code(evn->getEvent_Type_Code()+token->getLexeme()); } void Semantico::executeAction49(const Token *token) throw (SemanticError ){ evn->setRecorded_Date_Time(evn->getRecorded_Date_Time()+token->getLexeme()); } void Semantico::executeAction50(const Token *token) throw (SemanticError ){ evn->setDate_Time_Planned_Event(evn->getDate_Time_Planned_Event()+token->getLexeme()); } void Semantico::executeAction51(const Token *token) throw (SemanticError ){ evn->setEvent_Reason_Code(evn->getEvent_Reason_Code()+token->getLexeme()); } void Semantico::executeAction52(const Token *token) throw (SemanticError ){ evn->setOperator_ID(evn->getOperator_ID()+token->getLexeme()); } void Semantico::executeAction53(const Token *token) throw (SemanticError ){ evn->setEvent_Occurred(evn->getEvent_Occurred()+token->getLexeme()); } void Semantico::executeAction54(const Token *token) throw (SemanticError ){ evn->setFim_Linha(token->getLexeme()); } void Semantico::executeAction55(const Token *token) throw (SemanticError ){ if (msa== NULL){ msa= new CMSA(); } msa->setSegment_ID(msa->getSegment_ID()+token->getLexeme()); }
184
void Semantico::executeAction56(const Token *token) throw (SemanticError ){ msa->setAcknowledgment_Code(msa->getAcknowledgment_Code()+token->getLexeme()); } void Semantico::executeAction57(const Token *token) throw (SemanticError ){ msa->setMessage_Control_ID(msa->getMessage_Control_ID()+token->getLexeme()); } void Semantico::executeAction58(const Token *token) throw (SemanticError ){ msa->setText_Message(msa->getText_Message()+token->getLexeme()); } void Semantico::executeAction59(const Token *token) throw (SemanticError ){ msa->setExpected_Sequence_Number(msa->getExpected_Sequence_Number()+token->getLexeme()); } void Semantico::executeAction60(const Token *token) throw (SemanticError ){ msa->setDelayed_Acknowledgment_Type(msa->getDelayed_Acknowledgment_Type()+token->getLexeme()); } void Semantico::executeAction61(const Token *token) throw (SemanticError ){ msa->setError_Condition(msa->getError_Condition()+token->getLexeme()); } void Semantico::executeAction62(const Token *token) throw (SemanticError ){ msa->setFim_Linha(token->getLexeme()); } void Semantico::executeAction114(const Token *token) throw (SemanticError ){ if (qrd== NULL){ qrd= new CQRD(); } qrd->setSegment_ID(qrd->getSegment_ID()+token->getLexeme()); } void Semantico::executeAction115(const Token *token) throw (SemanticError ){ qrd->setQuery_date_time(qrd->getQuery_date_time()+token->getLexeme()); } void Semantico::executeAction116(const Token *token) throw (SemanticError ){ qrd->setQuery_format_code(qrd->getQuery_format_code()+token->getLexeme()); } void Semantico::executeAction117(const Token *token) throw (SemanticError ){ qrd->setQuery_priority(qrd->getQuery_priority()+token->getLexeme()); } void Semantico::executeAction118(const Token *token) throw (SemanticError ){ qrd->setQuery_ID(qrd->getQuery_ID()+token->getLexeme()); } void Semantico::executeAction119(const Token *token) throw (SemanticError ){ qrd->setDeferred_response_type(qrd->getDeferred_response_type()+token->getLexeme()); }
185
void Semantico::executeAction120(const Token *token) throw (SemanticError ){ qrd->setDeferred_response_date_time(qrd->getDeferred_response_date_time()+token->getLexeme()); } void Semantico::executeAction121(const Token *token) throw (SemanticError ){ qrd->setQuantity_limited_request(qrd->getQuantity_limited_request()+token->getLexeme()); } void Semantico::executeAction122(const Token *token) throw (SemanticError ){ qrd->setWho_subject_filter(qrd->getWho_subject_filter()+token->getLexeme()); } void Semantico::executeAction123(const Token *token) throw (SemanticError ){ qrd->setWhat_subject_filter(qrd->getWhat_subject_filter()+token->getLexeme()); } void Semantico::executeAction124(const Token *token) throw (SemanticError ){ qrd->setWhat_department_data_code(qrd->getWhat_department_data_code()+token->getLexeme()); } void Semantico::executeAction125(const Token *token) throw (SemanticError ){ qrd->setWhat_data_code_value_qualifier(qrd->getWhat_data_code_value_qualifier()+
token->getLexeme()); } void Semantico::executeAction126(const Token *token) throw (SemanticError ){ qrd->setQuery_results_level(qrd->getQuery_results_level()+token->getLexeme()); } void Semantico::executeAction127(const Token *token) throw (SemanticError ){ qrd->setFim_Linha(token->getLexeme()); } void Semantico::executeAction128(const Token *token) throw (SemanticError ){ qrf= new CQRF(); qrf->setSegment_ID(qrf->getSegment_ID()+token->getLexeme()); } void Semantico::executeAction129(const Token *token) throw (SemanticError ){ qrf->setWhere_Subject_Filter(qrf->getWhere_Subject_Filter()+token->getLexeme()); } void Semantico::executeAction130(const Token *token) throw (SemanticError ){ qrf->setWhen_Data_Start_Date_Time(qrf->getWhen_Data_Start_Date_Time()+token->getLexeme()); } void Semantico::executeAction131(const Token *token) throw (SemanticError ){ qrf->setWhen_Data_End_Date_Time(qrf->getWhen_Data_End_Date_Time()+token->getLexeme()); } void Semantico::executeAction132(const Token *token) throw (SemanticError ){ qrf->setWhat_User_Qualifier(qrf->getWhat_User_Qualifier()+token->getLexeme()); } void Semantico::executeAction133(const Token *token) throw (SemanticError ){ qrf->setOther_QRY_Subject_Filter(qrf->getOther_QRY_Subject_Filter()+token->getLexeme()); }
186
void Semantico::executeAction134(const Token *token) throw (SemanticError ){ qrf->setWhich_Date_Time_Qualifier(qrf->getWhich_Date_Time_Qualifier()+token->getLexeme()); } void Semantico::executeAction135(const Token *token) throw (SemanticError ){ qrf->setWhich_Date_Time_Status_Qualifier(qrf->getWhich_Date_Time_Status_Qualifier()+token->getLexeme()); } void Semantico::executeAction136(const Token *token) throw (SemanticError ){ qrf->setDate_Time_Selection_Qualifier(qrf->getDate_Time_Selection_Qualifier()+token->getLexeme()); } void Semantico::executeAction137(const Token *token) throw (SemanticError ){ qrf->setWhen_Quantity_Timing_Qualifier(qrf->getWhen_Quantity_Timing_Qualifier()+token->getLexeme()); } void Semantico::executeAction138(const Token *token) throw (SemanticError ){ qrf->setFim_Linha(token->getLexeme()); } void Semantico::executeAction160(const Token *token) throw (SemanticError ){ dsp= CDSP(); dsp.setSegment_ID_DSP(dsp.getSegment_ID_DSP()+token->getLexeme()); } void Semantico::executeAction161(const Token *token) throw (SemanticError ){ dsp.setDisplay_Level(dsp.getDisplay_Level()+token->getLexeme()); } void Semantico::executeAction162(const Token *token) throw (SemanticError ){ dsp.setData_Line(dsp.getData_Line()+token->getLexeme()); } void Semantico::executeAction163(const Token *token) throw (SemanticError ){ dsp.setLogical_Break_Point(dsp.getLogical_Break_Point()+token->getLexeme()); } void Semantico::executeAction164(const Token *token) throw (SemanticError ){ dsp.setResult_ID(dsp.getResult_ID()+token->getLexeme()); } void Semantico::executeAction165(const Token *token) throw (SemanticError ){ dsp.setFim_Linha(token->getLexeme()); vetorDSP.push_back(dsp); } void Semantico::executeAction173(const Token *token) throw (SemanticError ){ if (qak== NULL){ qak= new CQAK(); } qak->setSegment_ID_QAK(qak->getSegment_ID_QAK()+token->getLexeme()); } void Semantico::executeAction174(const Token *token) throw (SemanticError ){ qak->setQuery_Tag_QAK(qak->getQuery_Tag_QAK()+ token->getLexeme()); }
187
void Semantico::executeAction175(const Token *token) throw (SemanticError ){ qak->setQuery_Response_Status(qak->getQuery_Response_Status()+token->getLexeme()); } void Semantico::executeAction176(const Token *token) throw (SemanticError ){ qak->setFim_Linha(token->getLexeme()); } CMSH Semantico::enviaObjetoMSH(){ return *msh; } CEVN Semantico::enviaObjetoEVN(){ return *evn; } CPID Semantico::enviaObjetoPID(){ return *pid; } CMSA Semantico::enviaObjetoMSA(){ return *msa; } CQRD Semantico::enviaObjetoQRD(){ return *qrd; } CQRF Semantico::enviaObjetoQRF(){ return *qrf; } vector<CDSP> Semantico::enviaObjetoDSP(){ return vetorDSP; } CQAK Semantico::enviaObjetoQAK(){ return *qak; }
Top Related