diff --git a/crates/sesam-vitale/.env.build.win b/crates/sesam-vitale/.env.build.win new file mode 100644 index 0000000..fb75b65 --- /dev/null +++ b/crates/sesam-vitale/.env.build.win @@ -0,0 +1,3 @@ +SESAM_FSV_VERSION=1.40.14 +SESAM_FSV_LIB_PATH="C:/Program Files/santesocial/fsv/${SESAM_FSV_VERSION}/lib" +SESAM_FSV_SSVLIB=ssvw64 diff --git a/crates/sesam-vitale/.env.win b/crates/sesam-vitale/.env.win new file mode 100644 index 0000000..6aabb32 --- /dev/null +++ b/crates/sesam-vitale/.env.win @@ -0,0 +1,2 @@ +SESAM_FSV_VERSION=1.40.14 +SESAM_INI_PATH=${ALLUSERSPROFILE}\\santesocial\\fsv\\${SESAM_FSV_VERSION}\\conf\\sesam.ini diff --git a/crates/sesam-vitale/.env.win.example b/crates/sesam-vitale/.env.win copy.example similarity index 98% rename from crates/sesam-vitale/.env.win.example rename to crates/sesam-vitale/.env.win copy.example index bcd5177..a87f211 100644 --- a/crates/sesam-vitale/.env.win.example +++ b/crates/sesam-vitale/.env.win copy.example @@ -1,2 +1,2 @@ -SESAM_FSV_VERSION=1.40.13 -SESAM_INI_PATH=${ALLUSERSPROFILE}\\santesocial\\fsv\\${SESAM_FSV_VERSION}\\conf\\sesam.ini +SESAM_FSV_VERSION=1.40.13 +SESAM_INI_PATH=${ALLUSERSPROFILE}\\santesocial\\fsv\\${SESAM_FSV_VERSION}\\conf\\sesam.ini diff --git a/crates/sesam-vitale/src/cartevitale.rs b/crates/sesam-vitale/src/cartevitale.rs new file mode 100644 index 0000000..ffc25cc --- /dev/null +++ b/crates/sesam-vitale/src/cartevitale.rs @@ -0,0 +1,450 @@ +use libc::{c_void, size_t}; +use std::ffi::CString; +use std::ptr; + +use crate::libssv::SSV_LireDroitsVitale; +use crate::ssv_memory::{decode_ssv_memory, Block}; + +#[derive(Debug, Default)] +pub struct CarteVitale { + donneesAssure: DonneesAssure, + serviceAMOFamille: ServiceAMOFamille, + donneesAccidentTravail: DonneesAccidentTravail, + donneesBeneficiaire: Vec, +} + +// 1. CB = Caractères Binaires » +// 2. CE = Caractères « Etendus » (ISO 8859-1) +// 3. CA = Caractères Alphanumériques (ASCII?) +// 4. CN = Caractères Numériques +#[derive(Debug, Default)] +struct DonneesAssure { + type_de_carte_vitale: char, // CA + numero_de_serie: String, // CN - 1 -> 20 + date_de_fin_de_validite: String, // CN - Format AAAAMMJJ0000 + donnees_administration_ruf1: char, // CN + donnees_administration_ruf2: String, // CA - 24 + donnees_ruf_administration: String, // CE - 10 + type_d_identification_du_porteur: char, // CA + numero_national_d_immatriculation: String, // CA - 13 + cle_du_nir: String, // CA - 2 + code_regime: String, // CN - 2 + caisse_gestionnaire: String, // CN - 3 + centre_gestionnaire: String, // CN - 4 + code_gestion: String, // CA - 2 + donnees_ruf_famille: String, // CE - 55 +} + + +#[derive(Debug, Default)] +struct ServiceAMOFamille { + code_service_amo_famille: String, // CN - 2 + date_de_debut_service_amo_famille: String, // CN - Format AAAAMMJJ0000 + date_de_fin_service_amo_famille: String, // CN - Format AAAAMMJJ0000 +} + + +#[derive(Debug, Default)] +struct DonneesAccidentTravail { + organisme_gestionnaire_risque_at: String, // CN - 9 + code_at: char, // CA - 2 + identifiant_at: String, // CN - 9 + organisme_gestionnaire_at1: String, // CN - 9 + code_at1: char, // CA - 2 + identifiant_at1: String, // CN - 9 + organisme_gestionnaire_at2: String, // CN - 9 + code_at2: char, // CA - 2 + identifiant_at2: String, // CN - 9 +} + + +#[derive(Debug, Default)] +struct DonneesBeneficiaire { + nom_usuel: String, // CE - 27 + nom_de_famille: String, // CE - 27 + prenom: String, // CE - 27 + adresse_ligne_1: String, // CE - 32 + adresse_ligne_2: String, // CE - 32 + adresse_ligne_3: String, // CE - 32 + adresse_ligne_4: String, // CE - 32 + adresse_ligne_5: String, // CE - 32 + nir_certifie: String, // CA - 13 + cle_du_nir_certifie: String, // CN - 2 + date_de_certification_du_nir: String, // CN - Format AAAAMMJJ0000 + date_de_naissance: String, // CN - Format AAAAMMJJ0000 + rang_de_naissance: String, // CN - 1 + qualite: String, // CN - 2 + code_service_amo_beneficiaire: char, // CA - 2 + date_de_debut_service_amo: String, // CN - Format AAAAMMJJ0000 + date_de_fin_service_amo: String, // CN - Format AAAAMMJJ0000 + donnees_ruf_amo: String, // CE - 30 + periodeDroitsAMO: Vec, + periodeCodeCouverture: Vec, + donneesMutuelle: DonneesMutuelle, + donneesComplementaire: DonneesComplementaire, + donneesRUFBeneficiaireComplementaire: DonneesRUFBeneficiaireComplementaire, +} + + +#[derive(Debug, Default)] +struct PeriodeDroitsAMO { + date_de_debut_droits_amo: String, // CN - Format AAAAMMJJ0000 + date_de_fin_droits_amo: String, // CN - Format AAAAMMJJ0000 +} + + +#[derive(Debug, Default)] +struct PeriodeCodeCouverture { + date_de_debut_code_couverture: String, // CN - Format AAAAMMJJ0000 + date_de_fin_code_couverture: String, // CN - Format AAAAMMJJ0000 + code_ald: String, // CN - 1 + code_situation: String, // CN - 4 +} + + +#[derive(Debug, Default)] +struct DonneesMutuelle { + identification_mutuelle: String, // CN - 8 + garanties_effectives: String, // CA - 8 + indicateur_de_traitement_mutuelle: String, // CN - 2 + type_de_services_associes: char, // CA - 1 + services_associes_au_contrat: String, // CA - 17 + code_aiguillage_sts: char, // CA - 1 + periodeDroitsMutuelle: Vec, +} + + +#[derive(Debug, Default)] +struct PeriodeDroitsMutuelle { + date_de_debut_droits_mutuelle: String, // CN - Format AAAAMMJJ0000 + date_de_fin_droits_mutuelle: String, // CN - Format AAAAMMJJ0000 +} + + +#[derive(Debug, Default)] +struct DonneesComplementaire { + numero_complementaire_b2: String, // CA - 10 + numero_complementaire_edi: String, // CA - 19 + numero_adherent_amc: String, // CA - 8 + indicateur_de_traitement_amc: String, // CN - 2 + date_de_debut_validite_amc: String, // CN - Format AAAAMMJJ0000 + date_de_fin_validite_amc: String, // CN - Format AAAAMMJJ0000 + code_routage_flux_amc: String, // CA - 2 + identifiant_hote: String, // CA - 3 + nom_de_domaine_amc: String, // CE - 20 + code_aiguillage_sts: char, // CA - 1 + type_de_services_associes: char, // CA - 1 + services_associes_au_contrat: String, // CE - 17 +} + +#[derive(Debug, Default)] +struct DonneesRUFBeneficiaireComplementaire { + donnees_ruf_beneficiaire_complementaire: String, // CE - 115 +} + + +pub fn LireDroitsVitale(lecteurPS: &str, lecteurVitale: &str, codePorteurPS: &str, dateConsultation: &str) -> Result { + let resource_ps = CString::new(lecteurPS).expect("CString::new failed"); + let resource_vitale = CString::new(lecteurVitale).expect("CString::new failed"); + let card_number = CString::new(codePorteurPS).expect("CString::new failed"); + let date_consultation = CString::new(codePorteurPS).expect("CString::new failed"); + + let mut buffer: *mut c_void = ptr::null_mut(); + let mut size: size_t = 0; + let mut hex_values: &[u8] = &[]; + unsafe { + let result = SSV_LireDroitsVitale( + resource_ps.as_ptr(), + resource_vitale.as_ptr(), + card_number.as_ptr(), + date_consultation.as_ptr(), + &mut buffer, + &mut size, + ); + println!("SSV_LireCartePS result: {}", result); + + if !buffer.is_null() { + hex_values = std::slice::from_raw_parts(buffer as *const u8, size); + libc::free(buffer); + } + } + let groups = decode_ssv_memory(hex_values, hex_values.len()); + decode_carte_vitale(groups) +} + +fn decode_carte_vitale(groups: Vec) -> Result { + let mut cartevitale = CarteVitale::default(); + + // let lastBlock :u16; + // let lastField :u16; + + for group in groups { + for field in group.content { + + match (group.id, field.id) { + (101, 1) => { + let byte = field.content[0]; + cartevitale.donneesAssure.type_de_carte_vitale = byte as char; + } + (101, 2) => { + cartevitale.donneesAssure.numero_de_serie = + String::from_utf8_lossy(field.content).to_string(); + } + (101, 3) => { + cartevitale.donneesAssure.date_de_fin_de_validite = + String::from_utf8_lossy(field.content).to_string(); + } + (101, 4) => { + let byte = field.content[0]; + cartevitale.donneesAssure.donnees_administration_ruf1 = byte as char; + } + (101, 5) => { + cartevitale.donneesAssure.donnees_administration_ruf2 = + String::from_utf8_lossy(field.content).to_string(); + } + (101, 6) => { + cartevitale.donneesAssure.donnees_ruf_administration = + String::from_utf8_lossy(field.content).to_string(); + } + (101, 7) => { + let byte = field.content[0]; + cartevitale.donneesAssure.type_d_identification_du_porteur = byte as char; + } + (101, 8) => { + cartevitale.donneesAssure.numero_national_d_immatriculation = + String::from_utf8_lossy(field.content).to_string(); + } + (101, 9) => { + cartevitale.donneesAssure.cle_du_nir = + String::from_utf8_lossy(field.content).to_string(); + } + (101, 10) => { + cartevitale.donneesAssure.code_regime = + String::from_utf8_lossy(field.content).to_string(); + } + (101, 11) => { + cartevitale.donneesAssure.caisse_gestionnaire = + String::from_utf8_lossy(field.content).to_string(); + } + (101, 12) => { + cartevitale.donneesAssure.centre_gestionnaire = + String::from_utf8_lossy(field.content).to_string(); + } + (101, 13) => { + cartevitale.donneesAssure.code_gestion = + String::from_utf8_lossy(field.content).to_string(); + } + (101, 14) => { + cartevitale.donneesAssure.donnees_ruf_famille = + String::from_utf8_lossy(field.content).to_string(); + } + (102, 1) => { + cartevitale.serviceAMOFamille.code_service_amo_famille = + String::from_utf8_lossy(field.content).to_string(); + } + (102, 2) => { + cartevitale.serviceAMOFamille.date_de_debut_service_amo_famille = + String::from_utf8_lossy(field.content).to_string(); + } + (102, 3) => { + cartevitale.serviceAMOFamille.date_de_fin_service_amo_famille = + String::from_utf8_lossy(field.content).to_string(); + } + (103, 1) => { + cartevitale.donneesAccidentTravail.organisme_gestionnaire_risque_at = + String::from_utf8_lossy(field.content).to_string(); + } + (103, 2) => { + let byte = field.content[0]; + cartevitale.donneesAccidentTravail.code_at = byte as char; + } + (103, 3) => { + cartevitale.donneesAccidentTravail.identifiant_at = + String::from_utf8_lossy(field.content).to_string(); + } + (103, 4) => { + cartevitale.donneesAccidentTravail.organisme_gestionnaire_at1 = + String::from_utf8_lossy(field.content).to_string(); + } + (103, 5) => { + let byte = field.content[0]; + cartevitale.donneesAccidentTravail.code_at1 = byte as char; + } + (103, 6) => { + cartevitale.donneesAccidentTravail.identifiant_at1 = + String::from_utf8_lossy(field.content).to_string(); + } + (103, 7) => { + cartevitale.donneesAccidentTravail.organisme_gestionnaire_at2 = + String::from_utf8_lossy(field.content).to_string(); + } + (103, 8) => { + let byte = field.content[0]; + cartevitale.donneesAccidentTravail.code_at2 = byte as char; + } + (103, 9) => { + cartevitale.donneesAccidentTravail.identifiant_at2 = + String::from_utf8_lossy(field.content).to_string(); + } + _ =>{} //{ + // return Err(format!( + // "Unknown (group, field) pair: ({}, {})", + // group.id, field.id + // )) + // } + } + } + } + Ok(cartevitale) +} + +// #[cfg(test)] +// mod test_decode_carte_ps { +// use super::*; + +// #[test] +// fn test_francoise_pharmacien0052419() { +// let bytes: &[u8] = &[ +// 0, 1, 51, // Block 01, Content size 51 +// 1, 48, // Field 01, Content size 1 +// 1, 56, // Field 02, Content size 1 +// 11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52, // Field 03, Content size 11 +// 1, 52, // Field 04, Content size 1 +// 2, 50, 50, // Field 05, Content size 2 +// 17, 80, 72, 65, 82, 77, 65, 67, 73, 69, 78, 48, 48, 53, 50, 52, 49, +// 57, // Field 06, Content size 17 +// 9, 70, 82, 65, 78, 67, 79, 73, 83, 69, // Field 07, Content size 9 +// 1, 84, // Field 08, Content size 1 +// 0, 2, 83, // Block 02, Content size 83 +// 1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24, +// 80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49, +// 57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49, +// 48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, +// ]; +// let blocks = decode_ssv_memory(bytes, bytes.len()); +// let carte_ps = decode_carte_ps(blocks).unwrap(); + +// assert_eq!(carte_ps.titulaire.type_de_carte_ps, "0"); +// assert_eq!(carte_ps.titulaire.type_d_identification_nationale, "8"); +// assert_eq!( +// carte_ps.titulaire.numero_d_identification_nationale, +// "99700524194" +// ); +// assert_eq!( +// carte_ps.titulaire.cle_du_numero_d_identification_nationale, +// "4" +// ); +// assert_eq!(carte_ps.titulaire.code_civilite, "22"); +// assert_eq!(carte_ps.titulaire.nom_du_ps, "PHARMACIEN0052419"); +// assert_eq!(carte_ps.titulaire.prenom_du_ps, "FRANCOISE"); +// assert_eq!(carte_ps.titulaire.categorie_carte, 'T'); + +// assert_eq!(carte_ps.situations.len(), 1); +// assert_eq!( +// carte_ps.situations[0].numero_logique_de_la_situation_de_facturation_du_ps, +// 1 +// ); +// assert_eq!(carte_ps.situations[0].mode_d_exercice, "0"); +// assert_eq!(carte_ps.situations[0].statut_d_exercice, "1"); +// assert_eq!(carte_ps.situations[0].secteur_d_activite, "86"); +// assert_eq!(carte_ps.situations[0].type_d_identification_structure, "1"); +// assert_eq!( +// carte_ps.situations[0].numero_d_identification_structure, +// "0B0221958" +// ); +// assert_eq!( +// carte_ps.situations[0].cle_du_numero_d_identification_structure, +// "8" +// ); +// assert_eq!( +// carte_ps.situations[0].raison_sociale_structure, +// "PHARMACIE DU CENTRE22195" +// ); +// assert_eq!( +// carte_ps.situations[0].numero_d_identification_de_facturation_du_ps, +// "00202419" +// ); +// assert_eq!( +// carte_ps.situations[0].cle_du_numero_d_identification_de_facturation_du_ps, +// "8" +// ); +// assert_eq!( +// carte_ps.situations[0].numero_d_identification_du_ps_remplaçant, +// "" +// ); +// assert_eq!( +// carte_ps.situations[0].cle_du_numero_d_identification_du_ps_remplaçant, +// "0" +// ); +// assert_eq!(carte_ps.situations[0].code_conventionnel, "1"); +// assert_eq!(carte_ps.situations[0].code_specialite, "50"); +// assert_eq!(carte_ps.situations[0].code_zone_tarifaire, "10"); +// assert_eq!(carte_ps.situations[0].code_zone_ik, "00"); +// assert_eq!(carte_ps.situations[0].code_agrement_1, "0"); +// assert_eq!(carte_ps.situations[0].code_agrement_2, "0"); +// assert_eq!(carte_ps.situations[0].code_agrement_3, "0"); +// assert_eq!( +// carte_ps.situations[0].habilitation_à_signer_une_facture, +// "1" +// ); +// assert_eq!(carte_ps.situations[0].habilitation_à_signer_un_lot, "1"); +// } + +// #[test] +// fn test_multiple_situations() { +// let bytes: &[u8] = &[ +// 0, 1, 51, // Block 01, Content size 51 +// 1, 48, 1, 56, 11, 57, 57, 55, 48, 48, 53, 50, 52, 49, 57, 52, 1, 52, 2, 50, 50, 17, 80, +// 72, 65, 82, 77, 65, 67, 73, 69, 78, 48, 48, 53, 50, 52, 49, 57, 9, 70, 82, 65, 78, 67, +// 79, 73, 83, 69, 1, 84, 0, 2, 83, // Block 02, Content size 83 +// 1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24, +// 80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49, +// 57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49, +// 48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 0, 3, +// 83, // Block 03, Content size 83 +// 1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24, +// 80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49, +// 57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49, +// 48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 0, 4, +// 83, // Block 04, Content size 83 +// 1, 1, 1, 48, 1, 49, 2, 56, 54, 1, 49, 9, 48, 66, 48, 50, 50, 49, 57, 53, 56, 1, 56, 24, +// 80, 72, 65, 82, 77, 65, 67, 73, 69, 32, 68, 85, 32, 67, 69, 78, 84, 82, 69, 50, 50, 49, +// 57, 53, 8, 48, 48, 50, 48, 50, 52, 49, 57, 1, 56, 0, 1, 48, 1, 49, 2, 53, 48, 2, 49, +// 48, 2, 48, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, +// ]; +// let blocks = decode_ssv_memory(bytes, bytes.len()); +// let carte_ps = decode_carte_ps(blocks).unwrap(); + +// assert_eq!(carte_ps.situations.len(), 3); +// assert_eq!( +// carte_ps.situations[0].raison_sociale_structure, +// "PHARMACIE DU CENTRE22195" +// ); +// assert_eq!( +// carte_ps.situations[1].raison_sociale_structure, +// "PHARMACIE DU CENTRE22195" +// ); +// assert_eq!( +// carte_ps.situations[2].raison_sociale_structure, +// "PHARMACIE DU CENTRE22195" +// ); +// } + +// #[test] +// #[should_panic] +// fn test_missing_field() { +// todo!(); +// } + +// #[test] +// #[should_panic] +// fn test_unknown_group_field_pair() { +// todo!(); +// } + +// #[test] +// #[should_panic] +// fn test_invalid_field_format() { +// todo!(); +// } +// } diff --git a/crates/sesam-vitale/src/lib.rs b/crates/sesam-vitale/src/lib.rs index c575954..cd1c5ea 100644 --- a/crates/sesam-vitale/src/lib.rs +++ b/crates/sesam-vitale/src/lib.rs @@ -1,4 +1,5 @@ pub mod cps; +pub mod cartevitale; pub mod libssv; pub mod ssv_memory; pub mod ssvlib_demo; diff --git a/crates/sesam-vitale/src/libssv.rs b/crates/sesam-vitale/src/libssv.rs index 22ed690..94f9e71 100644 --- a/crates/sesam-vitale/src/libssv.rs +++ b/crates/sesam-vitale/src/libssv.rs @@ -19,5 +19,13 @@ extern "C" { ZDonneesSortie: *mut *mut c_void, TTailleDonneesSortie: *mut size_t, ) -> c_ushort; + pub fn SSV_LireDroitsVitale( + NomRessourcePS: *const c_char, + NomRessourceLecteur: *const c_char, + CodePorteurPS: *const c_char, + DateConsultation: *const c_char, + ZDonneesSortie: *mut *mut c_void, + TTailleDonneesSortie: *mut size_t, + ) -> c_ushort; } // TODO : replace void* by Rust struct : https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs diff --git a/crates/sesam-vitale/src/main.rs b/crates/sesam-vitale/src/main.rs index 479f7f0..b2319da 100644 --- a/crates/sesam-vitale/src/main.rs +++ b/crates/sesam-vitale/src/main.rs @@ -1,4 +1,5 @@ mod cps; +mod carteVitale; mod libssv; mod ssv_memory; mod ssvlib_demo; diff --git a/crates/sesam-vitale/src/ssvlib_demo.rs b/crates/sesam-vitale/src/ssvlib_demo.rs index b9ee114..c152a7b 100644 --- a/crates/sesam-vitale/src/ssvlib_demo.rs +++ b/crates/sesam-vitale/src/ssvlib_demo.rs @@ -7,7 +7,10 @@ use std::ffi::CString; use std::path::PathBuf; use std::ptr; + use crate::cps::lire_carte; +use crate::cartevitale::LireDroitsVitale; + use crate::libssv::{SSV_InitLIB2, SSV_LireConfig}; fn ssv_init_lib_2() { @@ -51,10 +54,15 @@ pub fn demo() { let code_pin = "1234"; let lecteur = "HID Global OMNIKEY 3x21 Smart Card Reader 0"; + let lecteurvitale = "HID Global OMNIKEY 3x21 Smart Card Reader 1"; + let dateconsultation ="20240813"; let carte_ps = lire_carte(code_pin, lecteur).unwrap(); println!("CartePS: {:#?}", carte_ps); ssv_lire_config(); + let carte_vitale = LireDroitsVitale(lecteur, lecteurvitale, code_pin, dateconsultation).unwrap(); + println!("carte_vitale: {:#?}", carte_vitale); + println!("-----------------------------------------"); } diff --git a/crates/sesam-vitale/src/win/fsv/ssvw64.def b/crates/sesam-vitale/src/win/fsv/ssvw64.def index 4d66637..21b0e7c 100644 --- a/crates/sesam-vitale/src/win/fsv/ssvw64.def +++ b/crates/sesam-vitale/src/win/fsv/ssvw64.def @@ -3,3 +3,4 @@ EXPORTS SSV_InitLIB2 SSV_LireCartePS SSV_LireConfig + SSV_LireDroitsVitale \ No newline at end of file