Gestion de fichiers images dans Springboot

La gestion de l’upload de fichiers images dans Springboot s’appuie sur plusieurs classes et fichiers.

FileUploadController agit comme un contrôleur qui va recevoir la demande d’upload de fichier (via l’url /upload) et la demande d’afficher l’image ( via l’url /files/{filename:.+} ). nCette classe contient une référence sur une classe FileSystemStorageService implementant l’interface StorageService.

StorageService est une simple interface qui permet de définir les actions classiques d’un service Spring de stockage.

FileSystemStorageService implémente de façon réelle l’interface StorageService pour la gestion du lieu de stockage des fichiers. Par ailleurs, pour une raison de sécurité, on peut stocker les fichiers uploadés dans un dossier dont l’accès est protégé pour éviter l’upload de fichiers critiques sur le serveur en devinant l’URL.

upload.html est un fichier gérant le formulaire HTML permettant d’envoyer un fichier simplement sans javascript encore une fois pour simplifier cet exemple.

Après les liaisons entre les fichiers, voici le contenu très simplifié de ces derniers:

<!DOCTYPE html><html lang="en"><head>
  <title>upload</title>
<body>
<form method="post" enctype="multipart/form-data" action="/upload">
 <div>
   <label for="file">Sélectionner le fichier à envoyer</label>
   <input type="file" id="file" name="file" >
 </div> <div>
   <button>Envoyer</button>
 </div>
</form>
</body></html>

L’upload d’un fichier image se fait vers le contrôleur FileUploadController via l’action /upload, cette classe permet aussi l’affichage d’une ressource image dans le navigateur (il manque la gestion des exceptions à coder en plus) :

@Controller
public class FileUploadController {
  // Service spring de stockage des fichiers
  private final StorageService storageService;
  // Constructeur de la classe avec l'activation du service
  @Autowired
  public FileUploadController(StorageService storageService) {
    this.storageService = storageService;
  }

  @GetMapping("/files/{filename:.+}")
  @ResponseBody
   public ResponseEntity<Resource> serveFile(@PathVariable String filename) {
  Resource file = storageService.loadAsResource(filename);
  String mimeType = URLConnection.guessContentTypeFromName(file.getFilename());
   long contentLength = 0;
   try {
	contentLength = file.contentLength();
   } catch (IOException e) { }
		
   InputStream fileInputStream = null;
   try {   fileInputStream = file.getInputStream();
    } catch (IOException e) {}
		
   return ResponseEntity.ok().contentLength( contentLength )
	.contentType(MediaType.parseMediaType( mimeType ))
	.body(new InputStreamResource( fileInputStream ));
  }

//Upload d'un fichier 
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
   storageService.store(file);
   return "redirect:/";
}

}

L’interface StorageService permet de définir les méthodes à implémenter par FileSystemStorageService.

public interface StorageService {
    void init();
    String store(MultipartFile file);
    Stream<Path> loadAll();
    Path load(String filename);
    Resource loadAsResource(String filename);
    void deleteAll();
}

Enfin la classe FileSystemStorageService permet de créer un service qui va stocker les fichiers dans un dossier ./Stockage dans le projet Spring (la gestion des exceptions est à coder en plus):

@Service
public class FileSystemStorageService implements StorageService {
  private final Path rootLocation;
  @Autowired
  public FileSystemStorageService() {
    this.rootLocation = Paths.get("./Stockage/");
   }
  @Override
  @PostConstruct
  public void init() {
     try {
	Files.createDirectories(rootLocation);
    } catch (IOException e) {}
   }

  @Override
   public String store(MultipartFile file) {
String filename = StringUtils.cleanPath(file.getOriginalFilename());
  try {
  if (file.isEmpty()) {}  
  if (filename.contains("..")) { }
   try (InputStream inputStream = file.getInputStream()) {
    Files.copy(inputStream, this.rootLocation.resolve(filename), StandardCopyOption.REPLACE_EXISTING);
    }
   } catch (IOException e) {	}
   return filename;
}

@Override
public Stream<Path> loadAll() {
 try {
    return Files.walk(this.rootLocation, 1).filter(path -> !path.equals(this.rootLocation)).map(this.rootLocation::relativize);
 } catch (IOException e) {	return null; }

}

@Override
public Path load(String filename) {
  return rootLocation.resolve(filename);
}

@Override
public Resource loadAsResource(String filename) {
  Path file = load(filename);
  Resource resource = null;
  try {
     resource = new UrlResource(file.toUri());
   if (resource.exists() || resource.isReadable()) {
      return resource;
   } else {
     System.out.println("File not found " + filename); 
   }
  } catch (MalformedURLException e) { }
		return resource;
}

@Override
public void deleteAll() {
  FileSystemUtils.deleteRecursively(rootLocation.toFile());
}

}

Le code du contrôleur FileUploadController permet d’afficher le fichier image dans le navigateur, si on souhaite proposer en téléchargement un fichier, la méthode serveFile doit être modifiée:

    Resource file = storageService.loadAsResource(filename);
   return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getFilename() + "\"").body(file);

Merci à cet article (https://spring.io/guides/gs/uploading-files/) qui a permit la rédaction de ce contenu.

Publié dans Développement, IMT Mines Alès | Laisser un commentaire

Programmation par aspect avec Java

L’idée de base dans la programmation par Aspect est d’extraire du flot normal d’exécution d’un code les parties très techniques.

Dans Spring on a classiquement la partie du code qui gère le Controleur de l’entité Todo via cette méthode :

@RequestMapping(value = "/todos", method = RequestMethod.GET)
public String listeTodos() {
   
  System.out.println("listeTodos");

  List<Todo> listTodo = todoDao.listTodo();
  String json = listTodo.stream()
		.map( todo -> todo.toJson() )
		.collect( Collectors.joining( "," , "[" , "]") );
		
   return json;
}

Pourtant, on pourrait vouloir faire un log de cet appel en ajoutant classiquement un logger au sein de cette méthode listeTodos() sauf que le principe de la programmation par Aspect est de sortir cette partie technique en proposant l’interception de l’appel de cette méthode listeTodos avec un code qui s’exécute avant et/ou après l’appel à listeTodos.

De manière concrète on a cet enchainement :

  • Appel à une méthode technique avant listeTodos
  • Appel à listeTodos
  • Appel à une méthode technique après listeTodos (optionnel)

En premier lieu, il faut ajouter sur la méthode principal l’ajout des fonctionnaltiés de programmation par Aspect:

@SpringBootApplication
@Configuration
@EnableAspectJAutoProxy
public class DemoApplication {
}

Ensuite, il faut coder une classe qui va assurer l’interception avant et après dans cet exemple avec les critères d’interceptions :

@Component
@Aspect
public class RestLogAspect {
	    
    @Around("execution(* fr.ema.ceris.todo.demo.TodoControleur..*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public Object restLog(ProceedingJoinPoint joinPoint) throws Throwable {
    	
        // Partie @Before 
        Object returnValue = null;
        Throwable throwable = null;
        try {
            System.out.println("Appel à TodoControleur avant");
            
            returnValue = joinPoint.proceed();       

            System.out.println("Appel à TodoControleur après");
 
        } catch (Throwable throwable1) {
            throwable = throwable1;
            System.out.println(String.format("[RestLogAspect] : %s", throwable1.getMessage()));
        }


        //Partie  @After 
        
        return returnValue;
    }

}

L’annotation @Around indique que l’appel à cette méthode sera fait avant et après les méthodes ayant les 2 critères suivants (notation && entre les 2 critères) :

  • execution(* fr.ema.ceris.todo.demo.TodoControleur..*(..)) : une classe se trouvant dans ce package avec ce nom de classe
  • @annotation(org.springframework.web.bind.annotation.RequestMapping), une annotion de type RequestMapping

Messages de la console :

Appel à TodoControleur avant
listeTodos
Appel à TodoControleur après

Nous avons donc un affichage des messages dans la console avant et après l’appel à la méthode listeTodos ce qui permet d’instrumentaliser le code fonctionnel par un code technique.

La valeur de la variable returnValue est aussi le résultat de l’appel à la fonction listeTodos donc le contenu json de la variable que l’on pourrait donc formater ou compresser avant renvoi vers la partie front du site web.

On aurait aussi pu utiliser l’annotation @Before à la place de l’annotation @Around pour avoir une exécution avant la méthode listeTodos et/ou l’annoation @After pour avoir une exécution après la méthode listeTodos. L’annotation @Around est un excellent exemple car elle permet de disposer des effets avant et après l’appel.

Publié dans Développement | Laisser un commentaire

Visual Code xdebug et le rechargement automatique de Chrome

Dans la suite de l’article sur « Ligne de commande pour recharger une page Chrome« , j’ai eu l’objectif d’utiliser Visual Code pour lancer xdebug avec le langage de programmation PHP et avoir l’auto refresh de Chrome lié avec la sauvegarde d’un fichier dans Visual Code.

Etape 1 : xdebug

Xdebug doit être configuré et installer dans Wamp et l’édition du fichier php.ini d’Apache de l’outil Wamp devrait contenir ce genre d’informations

[xdebug]
zend_extension="c:/Developpement/wamp64-3.2.3/bin/php/php7.4.9/zend_ext/php_xdebug-2.9.6-7.4-vc15-x86_64.dll"
;PJE xdebug.remote_enable = off
xdebug.remote_enable=1

Correction pour la nouvelle version de xdebug le réglage s’appelle xdebug.mode a mettre donc en valeur debug au lieu de develop et il faut ajouter le réglage xdebug.start_with_request

[xdebug]
zend_extension="c:/Developpement/wamp64/bin/php/php7.4.26/zend_ext/php_xdebug-3.1.1-7.4-vc15-x86_64.dll"
;xdebug.mode allowed are : off develop coverage debug gcstats profile trace
;PJE xdebug.mode =develop
xdebug.mode=debug
xdebug.start_with_request

Toujours en xdebug 3 le port n’est plus 9000 mais 9003

Notez que je vous propose d’utiliser le « remote debug » de xdebug. Bien sûr, dans l’affichage de la page phpinfo, il faut vérifier que l’extension xdebug est bien activé :

Plus bas dans cette page on pourra vérifier que xdebug a les bons réglages dans la partie qui est dédié à xdebug.

Information que xdebug est actif dans la page phpinfo

Etape 2: Visual Code

Dans Visual Code, il faut activer l’extension PHP Debug pour disposer des outils de liaison entre Visual Code et xdebug. L’extension que j’ai utilisé est de Felix Becker : https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug.

Nous partons du principe que dans le dossier www de Wamp se trouve un sous-dossier dans lequel notre développement sera réalisé.

Pour éviter d’utiliser un fichier tasks.json, j’ai préparé une double configuration dans le fichier launch.json a créé dans le dossier de travail de notre projet. Ce fichier doit permettre d’indiquer à la fois à Visual Code d’écouter les appels de xdebug pour un débogage et de lancer un navigateur chrome avec les options de débogage à distance pour relancer l’affichage de la page en cas de modificaiton du programme php, d’une page html ou d’un fichier CSS ou Javascript.

Le fichier launch.json dont on modifiera les chemins de Chromium et l’URL d’appel en fonction des besoins du projet Web:

{
    // Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
    // Pointez pour afficher la description des attributs existants.
    // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [        
        {
            "type": "chrome",
            "request": "launch",
            "name": "Chromium",         
            "program": "C:/Users/pierre.jean/AppData/Local/Chromium/Application/chrome.exe",
            "runtimeArgs": [
               "--disable-web-security",
               "--auto-open-devtools-for-tabs",
               "--remote-debugging-port=9222",
               "http://127.0.0.1/${workspaceFolderBasename}/${relativeFile}?XDEBUG_SESSION_START"
             ],
            "webRoot": "${workspaceRoot}"
            },        
        {
            "name": "Xdebug",
            "type": "php",
            "request": "launch",
            "port": 9000,
        },
    ]
    ,"compounds": [
    {
      "name": "Xdebug & Chromium",
      "configurations": ["Chromium","Xdebug"],      
    }
  ]
}

L’appel au débogage doit être la première fois sur le choix « Xdebug & Chromium » pour lancer l’écoute sur l’URL http://127.0.0.1/dossier/index.php?XDEBUG_SESSION_START et ouvrir un navigateur Chromium avec les outils de développement Web ouvert.

Etape 3: Sauver un fichier relancer l’auto refresh de Chrome

Pour exploiter l’auto-refresh de Chrome/Chromium, j’ai pensé chercher l’équivalent des « builders » d’Eclipse, au final, la philosophie de Visual Code est d’utiliser une extension (https://marketplace.visualstudio.com/items?itemName=wk-j.save-and-run) dont l’objectif est de lancer automatiquement un programme debug.cmd qui est une variation de l’ancien programme.

L’extension Save And Run doit être programmé en modifiant le fichier settings.json se trouvant dans le dossier « C:\Users\pierre.jean\AppData\Roaming\Code\User\ » voici ma proposition de réglages pour déclencher le programme debug.cmd.


    "saveAndRun": {
      "commands": [
       {
          "match": "\\.(php|html|css|js)$",
          "cmd": "${workspaceRoot}/debug.cmd",
          "useShortcut": false,
          "silent": false
       }
      ]
    }    

Le fichier debug.cmd se trouvera dans le dossier de notre projet Web mais il pourrait être ailleurs. Ce fichier s’appuie encore une fois sur le programme websocat_win64.exe ( pour la communication par WebSocket), disponible sur cette page https://github.com/vi/websocat/releases/.

Bien sûr, devant la complexité de la programmation en batch/cmd je me suis posé la question de développer un programme complet capable de faire les appels WebSocket, capable de trier les objets json des réponses et pour simplifier l’installation selon les systèmes d’exploitations.

Etape 4: debug.cmd pour faire l’auto-refresh de la page

Le programme debug.cmd cherche dans la réponse à l’URL de débogage de Chrome http://127.0.0.1:9222/json un objet json contenant le mot « page » puis en cherchant l’URI pour le WebSocket avec le numéro unique qui permet de demander le rechargement de la page.

@echo off
 2 SETLOCAL ENABLEDELAYEDEXPANSION
 3 
 4 set count=1
 5 set pattern1="""type"": ""page"","
 6 set pattern2="webSocketDebuggerUrl"
 7 
 8 set findPage=0
 9 FOR /F "tokens=* USEBACKQ" %%F IN (`curl -s  http://127.0.0.1:9222/json`) DO (
10   REM find only page not web developper page with 'type' : 'page'
11   REM echo %%F | find %pattern1% 
12   for /f "tokens=2" %%I in ('echo %%F ^| find %pattern1%') DO (
13      if "%ERRORLEVEL%" == "0" (
14         set findPage=1
15      )
16   )
17 
18   REM find now webSocketDebuggerUrl
19   REM echo "!findPage!"
20   if !findPage!  == 1 (
21     REM find with 'webSocketDebuggerUrl' : 'ws://...'
22     for /f "tokens=2" %%L in ('echo %%F ^| find %pattern2%') DO (
23       if "%ERRORLEVEL%" == "0" (
24         set var=%%L
25       )
26     )
27   )
28 )
29 set str=%var:"devtoolsFrontendUrl":=%
30 set str=%str:,= %
31 set str=%str:"/devtools/inspector.html?=%
32 set str=%str: =%
33 set str=%str:"=%
34 set str=%str:ws=%
35 set str=!str:~1!
36 set str=ws://%str%
37 
38 echo {"id":0,"method":"Page.reload","params":{"ignoreCache":true}} | websocat_win64.exe -n1 %str% > NUL
39 
40 ENDLOCAL

J’ai essayé de rajouter une méthode pour trouver uniquement la page dont j’ai le nom passé en paramètre et gérer le cas de plusieurs onglets de Chrome. Malheureusement la performance est trop faible en batch/cmd. Il faudrait donc un vrai programme pour gérer cette situation et conserver en fait l’identifiant de l’onglet chrome à débogger une fois pour toute la session.

Conclusion

Donc xdebug est activé dans Wamp, Visual Code dispose d’une extension PHP Debug capable d’écouter les appels de xdebug. Visual Code a une configuration launch.json capable de lancer l’écoute et un navigateur qui va ouvrir une URL avec le paramètre XDEBUG_SESSION_START pour lancer xdebug.

Il manque que d’ajouter une extension Save and Run à Visual Code pour déclencher à la sauvegarde d’un fichier php du projet un auto-refresh de Chrome qui a été démarré avec les options de développement Web.

Le « Proof-of-concept » est réalisé grâce à cet enchainement. Mais pour commencer facilement, faite juste l’installation des 2 extensions, créer un sous-dossier dans le dossier www de Wamp, ajouter les fichiers dans ce dossier debug.cmd, launch.json et websocat_win64.exe et cela devrait commencer à fonctionner si votre fichier php.ini de Wamp a xdebug activé aussi.

Publié dans Développement | Laisser un commentaire

X-Sens Dot Sensor

Dans le cadre d’un projet de recherche, j’ai testé un senseur de chez X-Sens: X-Sens Dot :

Ce capteur doit permettre de remonter des informations accélération pour l’expérience. Devant la documentation en ligne sur ce dispositif et avec l’accord du support très réactif de l’entreprise X-sens, je pense qu’un tutoriel plus complet sur ce dispositif peut être utile au plus grand nombre.

Des informations actuellement que je dispose, je n’ai pas trouvé de solution pour connecter mon Windows 10 sur le X-Sens, le développement se fait via mon smartphone android (un samsung S10+) et avec Android Studio.

Je ne peux que vous conseiller de télécharger l’application officielle (xsensdot-apk-v2020.0.apk en Avril 2020) de X-Sens pour les X-Sens Dot et le SDK X-Sens Dot dans la rubrique Software/Wearable Sensor: https://www.xsens.com/software-downloads

Le SDK contient une documentation PDF et .chm et un fichier XsensDotSdk.aar qui a servi de base à ce tutoriel.

 

1/ Démarrage d’Android Studio

En premier lieu créons dans Android Studio un nouveau projet se basant sur une Activity vide:

Configuration du projet avec un nom, un nom de package, un dossier de stockage et surtout une version de SDK minimale à API 22 selon les recommandations de la documentation:

En premier ajoutons les droits spécifiques et nécessaire sur cette application, il est à noter que je déploie sur un smartphone réel et non vers l’émulateur. Je trouve que c’est plus rapide et plus efficace de développer sur un smartphone réel en suivant ces recommandations sur developer.android.com.

Ajoutons donc ces permissions, qu’il faudra activer aussi sur le smartphone individuellement pour Storage et Position (voir plus bas dans le tutoriel):

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 

2/ Importation de XsensDotSdk.aar

L’importation d’un module aar se fait en plusieurs étapes que j’ai découvert avec ce projet.

En premier lieu, il faut indiquer l’importation de ce module via le Menu File > New > New Module … puis choisir « Import .JAR/.AAR Package » , faire Next:

 

Indiquer le chemin vers le module XsensDotSdk.aar (pour le nom du subproject le laisser par défaut):

Ce n’est pas parce que le module est importé en tant que subproject que notre projet a une dépendance envers lui.

Donc, Menu File > Project Structure … > puis Dependencies > app (notre projet par défaut) puis le petit bouton + (Add Depency) puis choisir « Module Depency » :

 

 

 

Indiquons que l’on veut une dépendance vers XsensDotSDK, Ok pour valider cette fenêtre et ensuite de nouveau OK pour valider la fenêtre précédente:

Une phase de build se déclenche plus ou moins longue selon la performance de son ordinateur…

 

3/ Utilisation des classes du  XsensDotSdk.aar

Nous allons travailler dans ce tutoriel sur la MainActivity, ce n’est pas l’idéal mais pour un développement de test nous pouvons réaliser un premier essai.

Ajoutons l’implémentation des classes XsensDotDeviceCv et XSensDotScannerCb:

Nous avons une erreur d’importation que l’on règle en laissant la souris sur les 2 classes problématiques, IntelliJ nous propose d’importer les packages manquants:

Nous pouvons maintenant aussi fabriquer les implémentations manquantes pour les méthodes nécessaires aux classes XsensDotDeviceCv et XSensDotScannerCb en survolant avec notre curseur ces dernières:

 

L’ajout de toutes les méthodes se fait rapidement:

 

 

4/ Développement des fonctions de connexion au X-sens DOT

Nous allons donc coder dans la méthode onCreate encore une fois par simplicité puis nous détecterons que le smartphone a bien trouvé le X-Sens Dot en mettant un point d’arrêt dans la méthode onXsensDotScanner:

public void onXsensDotScanned(BluetoothDevice bluetoothDevice) {

Donc on code une propriété mxScanner

private XsensDotScanner mXsScanner;

Puis dans la méthode onCreate :

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    String version = XsensDotSdk.getSdkVersion();
    XsensDotSdk.setDebugEnabled(true);
    XsensDotSdk.setReconnectEnabled(true);
    mXsScanner = new XsensDotScanner(this.getApplicationContext(), this);
    mXsScanner.setScanMode(ScanSettings.SCAN_MODE_BALANCED);
    mXsScanner.startScan();
}

 

Enfin un bout de code pour récupérer les informations sur le device:

@Override
public void onXsensDotScanned(BluetoothDevice bluetoothDevice) {
    String name = bluetoothDevice.getName();
    String address = bluetoothDevice.getAddress();
}

Choisissez ensuite votre smartphone (il faut que le débuggage soit possible sur ce smartphone d’ailleurs cf developer.android.com) puis le bouton « débug app » :

Voici les indications de débugage avec les adresses (factice sur cet écran) de réponse:

Ajoutons maintenant la récupération des données par le capteur en ajoutant une nouvelle propriété et  en modification la méthode onXsensDotScanned:

Nouvelle propriété: xsDevice pour gérer la connexion à notre Xsens Dot

private XsensDotDevice xsDevice = null;

 

Nouvelle version de onXsensDotScanned

@Override
public void onXsensDotScanned(BluetoothDevice bluetoothDevice) {
    String name = bluetoothDevice.getName();
    String address = bluetoothDevice.getAddress();

    xsDevice = new XsensDotDevice(this.getApplicationContext(), bluetoothDevice,MainActivity.this);
    xsDevice.connect();
    xsDevice.startMeasuring();
}

Notre X-Sens Dot devrait capture les données dans la méthode onXsensDotDataChanged et l’envoyer dans le loger LogCat d’Android:

@Override
public void onXsensDotDataChanged(String address, XsensDotData xsensDotData) {
    Log.i( "XsensDot", "onXsensDotDataChanged " + address + " " + xsensDotData );
}

 

Nous lançons le débugage avec un point d’arrêt sur l’évènement onXsensDotDataChanged et un regard sur le LogCat dans IntelliJ:

Aucune donnée n’arrive sur la méthode onXsensDotDataChanged….

 

5/ Ajout des droits dans le smartphone

Les données n’arrivent pas car il faut ajouter les droits sur l’application directement sur ce dernier. j’avais eu déjà le problème avec les IMU embarquées dans le smartphone.

Il faut donc aller sur l’application, faire un appuie long, puis choisir « Infos sur l’appli. » puis dans l’écran « informations sur l’application » choisir le menu « Autorisations » puis accepter les 2 autorisations « Localisation » et « Stockage » pour enfin pour relancer l’application avec les droits complets:

Et donc récupération des données sur un point d’arrêt du débug quand les données arrivent:

 

6/ Liaison Xsens DOT – Smartphone Android – Code JAVA

Pour la récupération des données de l’accélération  angulaire sur X du capteur Xsens DOT, j’ai utilisé une liaison bluetooth additionnel de type Port COM à 115200 bauds avec un programme en Java sur mon PC:

 

Vous pouvez constater sur la vidéo ci-dessous que le résultat est quand même assez impressionnant, la rotation de ma main déclenche bien le déplacement du point rouge sur le smartphone et l’envoie au PC de l’information Gauche/Droite/Centre pour faire réaliser le même déplacement au point vert.

Il y a un délais bien sur entre la modification d’orientation et le déplacement du point. L’algorithme doit être améliorer pour diminuer ce délais.

7/ Nouvelle version des SDK de Xsens

Suite à la conférence d’Avril 2020 de Xsens, j’ai la possibilité de récupérer les données directement depuis le PC. Donc la nouvelle version doit être directement développée sur l’ordinateur pour récupérer les données via le protocole Bluetooth BLE.

Pour simplifier le développement sur Windows, j’ai développé le code en C#. En effet, mes anciens développement avec le bluetooth m’ont montré que ce langage est le plus proche du système d’exploitation sur windows; aucune surprise le langage C# appartient à Microsoft. Du coup, je pense que les ouvertures/fermetures des canaux de communication bluetooth BLE seront simplifiées par ce langage de programmation.

Mon conseil est d’en premier lieu de travailler à partir du document « Xsens DOT BLE Services Specifications » proposé en téléchargement par Xsens sur son site internet. Ce document indique bien une évolution dans l’accès à certaines données envoyés par la nouvelle version (1.3) des Xsens. Personnellement, je vais continuer à utiliser la version 1.0.0 car moins de données = plus simple à interpréter.

Nota: attention au format big-endiant/little-endian des données, j’ai passé un peu de temps en lisant les octets dans le mauvais ordre.
Ce n’est pas encore standardisé, heureusement la documentation indique « All the big-endian members will be changed to little-endian in the next firmware release ».

Le développement a du être mis en stand-by pour cause de projet en parallèle, mais les premiers résultats sont encourageant. Le standard Bluetooth BLE est bien accepté par le xSens DOT comme montre la vidéo suivante qui affiche les accélérations du composant sur un seul axe.

Démonstration de la liaison bleutooth BLE du Xsens avec un développement C# sur un PC Windows 10 Pro version 18362 (Indiquer ma version n’est pas anodin pour le développement bluetooth BLE).

Publié dans Développement | Laisser un commentaire

git sourcesup

https://sourcesup.renater.fr/account/
Mettre un mot de passe du compte unix
Vérifier le mot de passe: https://sourcesup.renater.fr/account/change_pw.php

Trouver l’URL
URL : https://pierre.jean@git.renater.fr/authscm/pierre.jean/git/rearm/rearm.git cf copie d’écran en dessous pour le trouver
login UNIX pierre.jean
le BON MDP de SOurcesup

Ouvrir GitHub Desktop, faire clone a repository du smart https
https://pierre.jean@git.renater.fr/authscm/pierre.jean/git/rearm/rearm.git

—–
Ne fonctionne plus ?

Commande
git init

git clone ssh://git@git.renater.fr:2222/nom_projet.git

git clone https://pierre.jean@git.renater.fr/authscm/pierre.jean/git/rearm/rearm.git

Ouvre fenêtre authentification pour cloner sauf…

git config –global user.email « pierre.jean@mines-ales.fr »
git config –global user.name « pierre.jean »
git add rearm
git commit -m « Message de validation »

Publié dans Développement | Laisser un commentaire

Taiga Docker surprises

Pour une première étape avec Docker, j’ai essayé d’installer le logiciel taiga via cet outil de container. La solution la plus classique serait de prendre une image ubuntu et de suivre l’installation de docker (Setup development installation taiga).

Sauf qu’après avoir suivi une formation de Docker, j’ai compris que l’intérêt de Docker ou du moins un intéret est dans les micro service soit d’avoir 5 containers au lieu d’un seul.


NOTE: Vous avez un problème pour impoter votre projet taiga dans taiga (par exemple après une mise à jours) ?

Taiga contient un utilitaire qui se trouve dans le cotnaineur taiga-back et qui permet de faire l’importation suivant cette commande:

/usr/bin/python3 manage.py load_dump dump.json p.jean@mines-ales.fr

Taiga et Docker

Taiga est un logiciel de gestion de projet agile de type assez classique avec kanban et sprint intégré. Ce que j’aime dans Taiga en plus qu’il soit open source est que l’on peut activer ou non les outils agile, par exemple commencé avec un simple kanban et ensuite rajouter des sprint et/ou un wiki et/ou un gestionnaire de bug.

Taiga n’a pas de version officielle de Docker. Plusieurs images sont disponibles mais j’avoue que parfois les personnes qui ont fait les images avaient des idées très précises de leur utilisation qui ne m’allait par.

Dans ma solution je souhaite que Docker puisse gérer les cinq containers avec l’avantage de pouvoir renforcer un container ou le remplacer au besoin :

  • taiga-db database postgreSQL
  • taiga-front pour la partie front web de Taiga
  • taiga-back pour la partie backoffice de Taiga
  • taiga-rabbit pour le gestionnaire de message dans Taiga
  • taga-events pour l’envoie des notifications dans Taiga via le navigateur
  • taiga-proxy pour le serveur front nginx dispatchant les requêtes.

L’image de docker de base

Je pars donc d’une image existante de docker. En fait, j’en essaye plusieurs avant de trouver une image qui correspond à ce que je souhaite soit avec une base de données et avec la partie notifications/messages.

La version suivante : https://github.com/docker-taiga/taiga semble être un bon compromis. Je télécharge donc le dépot github et je fais une installation sans problème sur un serveur linux de test.

Hors, cette installation ne fonctionne qu’une seule fois. Je n’arrive pas en relançant à récupérer le fonctionnement de mon serveur taiga. Après plusieurs essaies et avec l’obligation de devoir intégrer mes propres développements dans Taiga, je suis donc contraint de prépare une installation à partir du fichier docker-compose.yml.

Docker-taiga-pje

Pourquoi faire une version de taiga docker personnelles:

  • Je souhaite intégrer 1 fichier et une bibliothèque python en plus.
  • Je souhaite débugger avec un outil curl dans taiga-back et taiga-proxy
  • Je souhaite que le déploiement se fasse tout seul avec le minimum d’intervention de ma part
  • Le container taiga-back contient un serveur web nginx qui fait double emploie pour moi avec le serveur taiga-proxy en frontal
  • Monter en compétence avec Docker avec un exemple et des contraintes réelles
  • Docker sur Windows 10 avec tout les désavantages que l’on y trouve (et il y en a Docker est fait pour Linux et cela se voit)

La première étape que je vais faire est de créer un dossier pour le taiga-proxy et modifier le code de nginx.conf et nginx_ssl.conf pour que le nginx de ce container aille chercher le http://back:8000/. Cela suppose aussi la modificaiton du docker-compose.yml pour qu’il prenne en charge cette nouvelle image appelée taiga-proxy-pje.

Puis je faire la même modification pour le taiga-back pour qu’il prenne en charge de nouveau modules python et modifier le script de démarrage pour ne pas charger le serveur nginx. J’en profiterai pour que le port 8000 de ce container soit accessible pour faire le débogage avec curl installé dans ce container.

Etape 1 création de l’image taiga-proxy-pje

Téléchargement des fichiers du dépot git-hub depuis https://github.com/docker-taiga/proxy

Version initiale du fichier nginx.conf

server {
	server_name $TAIGA_HOST;
	listen 80;
	location ^~ /events {
		proxy_pass http://$EVENTS_HOST:8888/;
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_connect_timeout 7d;
		proxy_send_timeout 7d;
		proxy_read_timeout 7d;
	}
	location ^~ /api {
		include proxy_params;
		proxy_pass http://$TAIGA_BACK_HOST;
	}
	location ^~ /admin {
		include proxy_params;
		proxy_pass http://$TAIGA_BACK_HOST;
	}
	location ^~ /static {
		include proxy_params;
		proxy_pass http://$TAIGA_BACK_HOST;
	}
	location ^~ /media {
		include proxy_params;
		proxy_pass http://$TAIGA_BACK_HOST;
	}
	location / {
		include proxy_params;
		proxy_pass http://$TAIGA_FRONT_HOST;
	}
}

Version modifiée du fichier nginx.conf

server {
server_name $TAIGA_HOST;
listen 80;
location ^~ /events {
proxy_pass http://$EVENTS_HOST:8888/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection « upgrade »;
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
}
location ^~ /api {
include proxy_params;
proxy_pass http://$TAIGA_BACK_HOST:8000;
}
location ^~ /admin {
include proxy_params;
proxy_pass http://$TAIGA_BACK_HOST;
}
location ^~ /static {
include proxy_params;
proxy_pass http://$TAIGA_BACK_HOST;
}
location ^~ /media {
include proxy_params;
proxy_pass http://$TAIGA_BACK_HOST;
}
location / {
include proxy_params;
proxy_pass http://$TAIGA_FRONT_HOST;
}
}

Maintenant dans le dossier taiga-pje/proxy en ligne de commande pour construire l’image taiga-proxy-pje :

docker build -t taiga-proxy-pje .

Il faut ensuite modifier le fichier docker-compose.yml pour ne plus appeler l’image taiga-proxy mais l’image taiga-proxy-pje, donc après avoir téléchargé tous les fichiers du dossier taiga via git

git clone –depth=1 -b latest https://github.com/docker-taiga/taiga.git

Nous obtenons le dossier suivant:

Nous pouvons donc ensuite modifier le fichier docker-compose.yml:

version: ‘3’

services:
back:
image: taiga-back
container_name: taiga-back
restart: unless-stopped
depends_on:
– db
– events
networks:
– default
volumes:
– ./data/media:/taiga-media
– ./conf/back:/taiga-conf
env_file:
– variables.env

front:
image: dockertaiga/front
container_name: taiga-front
restart: unless-stopped
networks:
– default
volumes:
– ./conf/front:/taiga-conf
env_file:
– variables.env

db:
image: postgres:11-alpine
container_name: taiga-db
restart: unless-stopped
networks:
– default
env_file:
– variables.env
volumes:
– ./data/db:/var/lib/postgresql/data

rabbit:
image: dockertaiga/rabbit
container_name: taiga-rabbit
restart: unless-stopped
networks:
– default
env_file:
– variables.env

events:
image: dockertaiga/events
container_name: taiga-events
restart: unless-stopped
depends_on:
– rabbit
networks:
– default
env_file:
– variables.env

proxy:
image: taiga-proxy-pje
container_name: taiga-proxy
restart: unless-stopped
depends_on:
– back
– front
– events
networks:
– default
ports:
– 80:80
– 443:443
volumes:
#- ./cert:/taiga-cert
– ./conf/proxy:/taiga-conf
env_file:
– variables.env

networks:
default:

Pour fabriquer l’ensemble des containers en allant chercher les images, la magie de docker est appeler avec

cd taiga
docker-compose.exe up -d

Pour mémoire, le dossier taiga/ contient le docker-compose.yml qui va faire l’assemblage des images et le dossier proxy contient l’image spéciale docker-proxy-pje.

Rapidement, vous allez faire un fichier go.bat avec:

cd proxy
docker build -t taiga-proxy-pje .
cd ..
cd taiga
docker-compose.exe up -d
cd ..

et un purge.bat qui fait plusieurs actions: retire les fichiers indiquant que ce démarrage n’est pas la version initiale, arrête l’option redémarrage des container pour pouvoir les arrêter :

  • retire les fichiers .initial_setup.lock qui indiquent que la configuration a déjà été faite (cf processus de création des fichiers conf ci-dessous)
  • mettre à jours le critères restart à no pour me permettre d’arrêter les containers car sinon docker les redémarre automatiquement, vous pouvez aussi faire docker update –restart=np $(docker ps -q) pour empecher tous les containers en fonctionnement de redémarrer
  • effacer les containeurs
  • effacer les images
  • effacer les volumes non utilisé par aucun container, attention cela pourrait retirer aussi d’autres volumes qui ne serait pas lié à des containers
rm taiga/conf/back/.initial_setup.lock
rm taiga/conf/front/.initial_setup.lock
rm taiga/conf/proxy/.initial_setup.lock
docker update –restart=no $(docker ps -a -q –filter « name=taiga-proxy » –filter « name=taiga-events » –filter « name=taiga-front » –filter « name=taiga-rabbit » –filter « name=taiga-db »)
docker rm taiga-proxy taiga-back taiga-events taiga-front taiga-rabbit taiga-db
docker rmi taiga-proxy-pje dockertaiga/front dockertaiga/events dockertaiga/rabbit dockertaiga/back
docker volume prune –force

Attention c’est assez violent de retirer tous les volumes non utilisé. Dans mon cas je n’avais que les containers et images liés à taiga.

Processus de créations des fichiers

J’ai eu beaucoup de problème pour comprendre qui fabrique les fichiers dans le dossier taiga/conf. En effet, au premier démarrage des containers taiga-back, taiga-proxy, taiga-front, le script appelé par le RUN (habituellement start.sh) de chaque Dockerfile va créer les dossiers et fichiers de la configuraton pour qu’au redémarrage du container, ces fichiers soient utilisés au lieu de les recréer à chaque fois. Le problème est de créer un processus complet de fabrication et non pas de bricoler dans les fichiers de taiga/conf/. On peut modifier ces fichiers pour tester quelqus réglages de nginx par exemple mais il faut bien qu’à la fin on ai la totalité du processus from scratch

Enfin, j’ai un taiga proxy qui appelle sur le port 8000, le serveur django. Maintenant, il faut configurer le taiga-back pour ne pas utiliser le nginx inclus mais directement le django

taiga-back-pje

Création du dossier back et modification du fichier go.sh

cd proxy
docker build -t taiga-proxy-pje .
cd ..
cd back
docker build -t taiga-back-pje .
cd ..

cd taiga
docker-compose.exe up -d
cd ..

et purge.bat:

rm taiga/conf/back/.initial_setup.lock
rm taiga/conf/front/.initial_setup.lock
rm taiga/conf/proxy/.initial_setup.lock
docker update –restart=no $(docker ps -a -q –filter « name=taiga-proxy » –filter « name=taiga-events » –filter « name=taiga-front » –filter « name=taiga-rabbit » –filter « name=taiga-db »)
docker rm taiga-proxy taiga-back taiga-events taiga-front taiga-rabbit taiga-db
docker rmi taiga-proxy-pje dockertaiga/front dockertaiga/events dockertaiga/rabbit taiga-back-pje
docker volume prune –force

Dans notre dossier back, nous allons trouver:

d—– 11/06/2019 11:04 users <— Le dossier contenant le nouveau fichier python pour l’OTP
-a—- 13/06/2019 18:43 1225 config.py <— Le fichier de configuration de taiga back
-a—- 08/06/2019 14:52 1956 Dockerfile <— Le fichier Dockerfile où le travail est le plus important
-a—- 02/06/2019 17:26 1466 Dockerfile.v0 <— Fichier Dockerfile initial, pour voir entre Linux et Windows
-a—- 08/06/2019 14:17 157 ntpd.conf <— Le container n’a pas accès à l’heure, cf plus bas
-a—- 13/02/2019 10:26 74 README.md <— Le fichier README de github
-a—- 10/06/2019 20:05 2015 start.sh <— Le nouveau fichier start.sh qui va bien changer

Il faut avec le terminal créer une nouvelle image docker-back que l’on va appeler docker-back-pje.

docker build -t taiga-back-pje .

Cette commande fabrique une nouvelle image de taiga-back. Sauf que sur windows, j’ai une erreur :

WARNING: Ignoring APKINDEX.b89edf6e.tar.gz: No such file or directory
WARNING: Ignoring APKINDEX.737f7e01.tar.gz: No such file or directory

Cette erreur vient d’un problème de dossier cache des packages d’alpine.

La solution est de modifier le dockerfile pour détruire ce cache et d’ajouter le package ca-certificates.
Je rajoute aussi le package « jq » pour avoir un outil d’affichage des données json lisible.

FROM alpine:latestENV TAIGA_HOST=taiga.lan \
TAIGA_SECRET=secret \
TAIGA_SCHEME=http \
POSTGRES_HOST=db \
POSTGRES_DB=taiga \
POSTGRES_USER=postgres \
POSTGRES_PASSWORD=password \
RABBIT_HOST=rabbit \
RABBIT_PORT=5672 \
RABBIT_USER=taiga \
RABBIT_PASSWORD=password \
RABBIT_VHOST=taiga \
STARTUP_TIMEOUT=15sWORKDIR /srv/taigaRUN rm -rf /var/cache/apk/* && rm -rf /tmp/* \
&& apk –no-cache add python3 gettext postgresql-dev libxslt-dev libxml2-dev libjpeg-turbo-dev zeromq-dev libffi-dev nginx \
&& apk add –no-cache –virtual .build-dependencies musl-dev python3-dev linux-headers git zlib-dev libjpeg-turbo-dev gcc ca-certificates jq \
&& mkdir logs \
&& git clone –depth=1 -b stable https://github.com/taigaio/taiga-back.git back && cd back \
&& sed -e ‘s/cryptography==.*/cryptography==2.3.1/’ -i requirements.txt \
&& pip3 install –upgrade pip \
&& pip3 install -r requirements.txt \
&& rm -rf /root/.cache \
&& apk del .build-dependencies \
&& rm /srv/taiga/back/settings/local.py.example \
&& rm /etc/nginx/conf.d/default.confEXPOSE 80

WORKDIR /srv/taiga/back

COPY config.py /tmp/taiga-conf/
COPY nginx.conf /etc/nginx/conf.d/
COPY start.sh /

VOLUME [« /taiga-conf », « /taiga-media »]

CMD [« /start.sh »]

Ok cela fabrique l’image, je peux modifier le fichier docker-compose.yml et recréer l’ensemble des images manquantes et des containers associés avec le script go.sh

version: ‘3’

services:
back:
image: taiga-back-pje
container_name: taiga-back
restart: unless-stopped
depends_on:
– db
– events
networks:
– default
volumes:
– ./data/media:/taiga-media
– ./conf/back:/taiga-conf
env_file:
– variables.env

front:
image: dockertaiga/front
container_name: taiga-front
restart: unless-stopped
networks:
– default
volumes:
– ./conf/front:/taiga-conf
env_file:
– variables.env

db:
image: postgres:11-alpine
container_name: taiga-db
restart: unless-stopped
networks:
– default
env_file:
– variables.env
volumes:
– ./data/db:/var/lib/postgresql/data

rabbit:
image: dockertaiga/rabbit
container_name: taiga-rabbit
restart: unless-stopped
networks:
– default
env_file:
– variables.env

events:
image: dockertaiga/events
container_name: taiga-events
restart: unless-stopped
depends_on:
– rabbit
networks:
– default
env_file:
– variables.env

proxy:
image: taiga-proxy-pje
container_name: taiga-proxy
restart: unless-stopped
depends_on:
– back
– front
– events
networks:
– default
ports:
– 80:80
– 443:443
volumes:
#- ./cert:/taiga-cert
– ./conf/proxy:/taiga-conf
env_file:
– variables.env

networks:
default:

Sauf que après un petit tour dans le container taiga-back fabriqué à partir de mon image taiga-back-pje

docker exec -it taiga-back sh

La date est complètement fausse sur Windows

En fait, docker windows ne peut accéder (avec la version que j’avais en mars 2019) à l’horloge système.
Il n’y a pas de problème sous Linux.

Comme j’ai besoins de cette date à jour pour la suite du projet. Je dois injecter un serveur de temps openntpd et le configurer dans l’image docker-back-pje

Nouveaux modifications du fichier Dockerfile

FROM alpine:latestENV TAIGA_HOST=taiga.lan \
TAIGA_SECRET=secret \
TAIGA_SCHEME=http \
POSTGRES_HOST=db \
POSTGRES_DB=taiga \
POSTGRES_USER=postgres \
POSTGRES_PASSWORD=password \
RABBIT_HOST=rabbit \
RABBIT_PORT=5672 \
RABBIT_USER=taiga \
RABBIT_PASSWORD=password \
RABBIT_VHOST=taiga \
STARTUP_TIMEOUT=15sWORKDIR /srv/taigaRUN rm -rf /var/cache/apk/* && rm -rf /tmp/* \
&& apk –no-cache add python3 gettext postgresql-dev libxslt-dev libxml2-dev libjpeg-turbo-dev zeromq-dev libffi-dev nginx \
&& apk add –no-cache –virtual .build-dependencies musl-dev python3-dev linux-headers git zlib-dev libjpeg-turbo-dev gcc ca-certificates jq openntpd \
&& mkdir logs \
&& cp /usr/share/zoneinfo/Europe/Paris /etc/localtime && echo « Europe/Paris » > /etc/timezone && renice -n-8 $(pidof ntpd -s) \
&& git clone –depth=1 -b stable https://github.com/taigaio/taiga-back.git back && cd back \
&& sed -e ‘s/cryptography==.*/cryptography==2.3.1/’ -i requirements.txt \
&& pip3 install –upgrade pip \
&& pip3 install -r requirements.txt \
&& rm -rf /root/.cache \
&& apk del .build-dependencies tzdata \
&& rm /srv/taiga/back/settings/local.py.example \
&& rm /etc/nginx/conf.d/default.confEXPOSE 80

WORKDIR /srv/taiga/back

COPY ntpd.conf /etc/ntpd.conf
COPY config.py /tmp/taiga-conf/
COPY nginx.conf /etc/nginx/conf.d/
COPY start.sh /

VOLUME [« /taiga-conf », « /taiga-media »]

CMD [« /start.sh »]

On note qu’il y a une injection de la timezone dans le fichier /etc/timezone pour indiquer le fuseau horaire

Bien sur le fichier ntpd.conf sur le disque dur a ces informations

# See ntpd.conf(5) and /etc/examples/ntpd.conf
listen on *server time1.google.com
server time2.google.com
server time3.google.com
server time4.google.com

On peut indiquer d’autres serveurs de temps dans le cas d’une structure qui aurait une politique locale des serveurs de temps

Je suis toujours dessus pour améliorer le processus…

Publié dans Développement | Laisser un commentaire

Accès manager-gui via Tomcat 8.5 dans Docker

Suite à l’installation de Tomcat 8.5 dans docker, je n’ai pas trouvé simplement comment accéder à l’interface de déploiement web dans Tomcat.

Il faut donc modifier les fichiers suivants:

/usr/local/tomcat/conf/tomcat-users.xml:

<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<user username="admin" password="password" roles="manager-gui,manager-script" />

Puis aller dans les réglages de l’application manager dans /usr/local/tomcat/webapps/manager/META-INF/context.xml et modifier le tag suivant :

 <Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />

Par ceci:

 <Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="\d+\.\d+\.\d+.\d+|127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />

Merci à cet article :

https://stackoverflow.com/questions/42692206/docker-tomcat-users-configuration-not-working

Pour Tomcat 9

le déploiement en Tomcat 9 peut se faire simplement en ligne de commande

docker run -d -p 8888:8080 --name tomcat9 tomcat:9.0.56-jdk17-temurin
docker cp DevWeb2021.war  tomcat9:/usr/local/tomcat/webapps/
Publié dans Développement | Laisser un commentaire

gvim et vimrc

Habituellement je n’utilise que la ligne suivante dans mon fichier vimrc qui gère la configuration par défaut de mon gvim/vim :

colorscheme slate

Juste le changement du thème et donc des couleurs par défaut de Gvim en choisissant un thème prédéfini.

J’avais besoin de disposer d’un Gvim ouvert au centre de mon écran de taille plutôt réduite déjà en mode édition avec un fichier vide par défaut. Du coup, j’ai installé le plugin wimproved.vim. Ce plugin a installer simplement, le fichier wimproved32.dll dans le dossier principal de vim, dans le dossier autoload le fichier autoload/wimproved.vim et dans le dossier plugin le fichier plugin/wimproved.vim.

Ensuite il faut configurer le fichier vimrc :

" Simplifier l'interface de gvim en retirant le menu et les icones
autocmd GUIEnter * silent! WToggleClean
" Centrer gvim avec une taille de 30% de l'écran
autocmd GUIEnter * silent! WCenter 30
" Degré de transparence de l'interface sur 255 niveau
autocmd GUIEnter * silent! WSetAlpha 220
" Couleur du thème
colorscheme slate

" test si c'est l'édition d'un nouveau fichier ou d'un fichier existant
" ces réglages ne sont appliqués qu'au nouveau fichier pas aux fichiers en éditon 
if argc()==0

  " cache les symboles ~ qui indiquent des lignes non existente
  hi NonText guifg=bg
  " fait une insertion vide par défaut pour retirer le message d'introduction 
  normal i 
  " mode insertion au lieu d'être mode commande comme habituellement
  start
endif


 

Vi / VIM / Gvim sont des outils très pratiques car disponiblent sur toutes les plateformes et les systèmes d’exploitations. Ils gèrent un nombre important type de format et ouvrent même des fichiers de tailles énormes sans sourciller. Je sais que l’apprentissage sur cet outil est compliqué (mode commande <-> mode édition) mais les manipulations de types expressions régulières et les plugins additionnels en font un outil extraordinaire.

Efface les lignes avec le mot profile quelque part:

:g/profile/d

Efface les lignes ne contenant par le mot profile quelque part:

:g!/profile/d

 

Scripts avec gVIM

Maintenant supposons que l’on souhaite appeler une commande classique qui efface les lignes vides d’un fichier.

On peut créer le fichier pje.vim par exemple dans le dossier de l’utilisateur (dans mon cas c:/Users/pierre.jean/pje.vim). Dans ce fichier, on indique cette ligne :

g/^$/d

Il suffit ensuite de l’appeler avec la commande source

:source ~/pje.vim

Ou avec le chemin complet

:source c:/Users/pierre.jean/pje.vim

Mais comment faire de cette ligne une fonction et l’appeler plusieurs fois surtout dans la nouvelle version Gvim 9. En premier lieu nous créons une nouvelle fonction avec le mot clef def dans le script pje.vim

vim9script 
# RemoveEnmptyLine
def RemoveEmptyLine()
   g/^$/d
enddef

La fonction RemoveEmptyLine est créée mais non appelée. Alors on pourrait ajouté un appel avec call RemoveEmptyLine au dessous mais nous voulons une commande et un raccourci pour l’appel de notre fonction ajoutons donc:

vim9script 
# RemoveEnmptyLine
def RemoveEmptyLine()
   g/^$/d
enddef


command! Rel :call RemoveEmptyLine()
nmap <silent>  <C-D>  :Rel<CR>

La commande Rel peut appeler la méthode RemoveEmptyLine et la dernière ligne lie la combinaison de CTRL+D avec la commande Rel.

Alors, je ne suis pas un expert dans les nouveaux scripts de gvim 9, je n’ai pas compris pourquoi ma nouvelle fonction de type def n’est pas appelable en mode commande, mais cette solution me va bien, soit une commande et un raccourci.

Publié dans Développement | Laisser un commentaire

Résultat du sondage de Stackoverflow 2017 et Infographie de Chooseyourboss 2018

Le sondage 2017 du site web de support informatique stackoverflow vient de sortir. Le sondage est très/trop complet notamment sur des questions hors du domaine du développement (le développeur a t’il des enfants par exemple). Vous pouvez le trouver à cette URL.

Je sors quelques points que je trouve intéressant et donc ce n’est que mon regard personnel sur ce sondage, chacun est libre d’en sortir ses propres impressions.

Et blog de Choose Your Boss a ajouté une infographie pour 2018

 

 

Comment se définit le développeur:

Finalement on peut dire que les sondés sont globalement des développeurs qui font un peu de tout en Front/Back/Full, finalement c’est le « Desktop or entreprise applications » qui montre bien que ce type de développement est assez faible sur ce site. Peut être que les développeurs de ce genre d’applications sont expérimentés et n’ont plus besoin de poser des questions.

 

La bataille des IDEs:

Le Visual Studio payant et gratuit semble enfoncé les autres, le trio des éditeurs Sublime Text/Vim/Notepad++ semble être très utilisé au final, le reste est très classique au final.

Les langages des développeurs:

Beaucoup de développement web en Front, donc JS+HTML+CSS sontles premier. On trouve les inaltérables SQL/Shell en plus. La tendance reste Java/Python/C#/PHP/C++/C.  J’ai indiqué les langages ayant plus de 10%, vous trouverez surement votre langage dans le sondage.

 

Les Framework des développeurs:

Node.js / Angular /react indique bien que le développement des solutions javascript a le vent en poupe. Ce qui est surprenant encore c’est le .Net Core qui est très important, une information que je n’avais pas personnellement.

Base de données utilisées

 

La seconde place de SQL Server est pour moi une surprise, le reste me parait assez classique comme représentation.

Les plateformes cible des développeurs:

Attention, ce n’est pas avec quel OS ils développent, il y a une réponse spécifique sur ce point. On voit bien que le classique c’est Linux/Windows, un peu d’android, de cluster AWS, Mac OS. Rasberry Pi et WordPress sont présentés comme des cibles cela indique bien la force de leur spécificité.

 

Les regroupement  des technologies pour les développeurs:

Très utile cela indique bien les liaisons outils/technologies/plateformes pour comprendre qu’elles sont les compétences liées.

 

 

Publié dans Développement | Laisser un commentaire

Arduino M0 pro et UART additionnel

Dans le cadre d’un développement sur un Arduino M0 Pro, je me retrouve a devoir brancher en protocole UART un module bluetooth et un lecteur RFID tout les deux en UART.

Comme je débute en Arduino, je branche le module bluetooth sur RT( broche 0) /TX (broche 1) de la carte :

 

Donc se pose la question d’avoir une entrée UART sur d’autres broches. J’avoue que je traine sur google et que je trouve enfin la bonne explication pour transformer les broches  10 en TX et 11 en RX.

Vous trouverez sur ce lien toutes les explications chez Adafruit mais pour ceux qui cherchent comme moi, voici le résumé avec le code:

#include "wiring_private.h"

Uart Serial2(&sercom1, 11, 10, SERCOM_RX_PAD_0, UART_TX_PAD_2);

void SERCOM1_Handler()
{
 Serial2.IrqHandler();
}

void setup()
{
  Serial2.begin(9600);
  pinPeripheral(10, PIO_SERCOM);
  pinPeripheral(11, PIO_SERCOM);
}


void loop()
{
 if (Serial2.available()){
   byte byteRead = Serial2.read();
   Serial2.write(byteRead);
   Serial.write( byteRead ); 
 }
}

Et voila, j’avoue que cela m’a permis d’entrer dans des détails du « M0 pro » et de son contrôleur ATSAMD21  mais enfin j’ai compris comment on peut refaire un UART de plus en cas de besoin.

Voila le montage final avec UART sur PIN 0 et 1, SDA & SCL branché et enfin le nouveau UART PIN 10 et PIN 11

Publié dans Développement | Laisser un commentaire