29 januari 2021 • februari 2, 2021 at 12:51 pm Qdraw

Hoe gebruik je templates met Azure Pipelines

Azure Devops is een Microsoft dienst waar mee code projecten beschikbaar kunnen gesteld worden voor anderen. In deze technische blog geef ik een introductie hoe ik templates hergebruik. De Azure Pipelines werken vrijwel met elke programmeertaal of projecttype. Deze tool combineert Continuous Integration (CI) and Continuous Delivery (CD) om continue het project te compileren en testen. Dit is een methode voor het frequent leveren van applicaties naar (eind)klanten waarbij vrijwel alle stappen geautomatiseerd zijn.

Met Azure Pipelines is het gebruikelijk om de build pipelines in yaml te definieren. Dit zijn tekst bestanden waarin alle stappen worden uitgeschreven. Deze yaml bestanden worden ingecheckt in het versie beheer systeem. Het voordeel van deze werkwijze is dat deze per branch verschillend kunnen zijn, zodat je veranderingen niet op een centraal moment moet doorvoeren.

Vooral bij een complexer project is handig om verschillende onderdelen her te gebruiken voor bijvoorbeeld verschillende onderdelen. In het onderstaande project zijn een aantal onderdelen. Het is namelijk niet altijd nodig dat alles wordt gebouwd, vooral als je er geen veranderingen in heb doorgevoerd.

Een belangrijk voordeel is om het op te splitsen is dat je verschillende templates kunt hergebruiken, dit voorkomt dubbele code en de naam van de bestanden geven al een overzicht van de taken die hierin worden uitgevoerd.

In het project dat ik hier als voorbeeld gebruik hou ik de volgende verdeling aan:

1. Server. De backend van de website, in ons geval is dat is het Sitecore platform
2. Client. Losse front-end frameworks die los staan van de backend.
3. Services. Losse Windows services, die achtergrond taken uitvoeren.
4. Develop CI. Dat is een combinatie van alle bovenstaande pipelines

See English version of this article on Medium

Hieronder een schermafbeelding van hoe de pipeline inrichting eruit ziet.



Een introductie van Azure Pipelines en het hergebruik van templates ; Pipeline screen Azure Devops ; azure, devops, vsts, pipelines, build

Develop CI

Ik begin hier bij de Develop CI omdat deze de verschillende onderdelen allemaal bevat. In Azure Devops klik in het pipeline venster op ‘New pipeline’. Vervolgens selecteer ik de locatie van de repository. Dit kan zowel in Azure Devops als op Github zijn. Dan worden er een aantal opties voorgesteld. Ik begin hier met de starter pipeline en kies een locatie. Voor nu ga ik voor de Develop CI uit startpunt: /azure-pipeline/develop-ci.yml Wanneer je op ‘Save and Run’ klikt dan wordt deze ingecheckt. Vervolgens wordt de pipeline uitgevoerd.



Een introductie van Azure Pipelines en het hergebruik van templates ; Review your pipeline Azure Devops ; azure, devops, vsts, pipelines, build, yaml, starter kit

Parallel lopende machines met Stages

In het (bovenstaande) starter voorbeeld wordt maar 1 machine tegelijk gebruikt. Voor een groter project is het handiger als onderdelen parallel lopen. Hier zijn wel kosten aan verbonden. Als je pipelines afgeschermd zijn en je gebruikt de gratis variant dan heb je 1 machine per organisatie beschikbaar met een maximum van 1800 minuten per maand. Dit kan veranderen maar dit is de huidige situatie van januari 2021.

De stages in de yaml pipeline zijn bijwijze van spreken de verschillende boeken en jobs de hoofdstukken. De jobs worden op verschillende machines uitgevoerd. De laatste stap ‘Merge’ wacht voordat de Client en Server stages zijn afgerond.

Hieronder de yaml pipeline voor /azure-pipeline/develop-ci.yml


trigger: none

pr:
- development

variables:
  buildPlatform: "Any CPU"
  buildConfiguration: "Release"
  webroot: "$(build.artifactstagingdirectory)/wwwroot"
  serverPool: "windows-latest"
  clientPool: "ubuntu-20.04"
  serverSitecoreArtifact: "server_sitecore"
  serverUnicornDataArtifact: "server_unicorn_sitecore_data"
  serverConfigCollectionArtifact: "server_config_collection"
  clientArtifact: "client"
  mergeArtifact: "merge"
  npm_config_cache: $(Pipeline.Workspace)/.npm

stages:
  - stage: Server
    dependsOn: []
    jobs:
      - template: /azure-pipeline/jobs/server_sitecore.yml
        parameters:
          pool: "$(serverPool)"
          artifact: "$(serverSitecoreArtifact)"

      - template: /azure-pipeline/jobs/server_config_collection.yml
        parameters:
          pool: "$(serverPool)"
          artifact: "$(serverConfigCollectionArtifact)"
          serverUnicornDataArtifact: "$(serverUnicornDataArtifact)"

  - stage: Client
    dependsOn: []
    jobs:
      - template: /azure-pipeline/jobs/client.yml
        parameters:
          pool: "$(clientPool)"
          artifact: "$(clientArtifact)"

  - stage: Services
    dependsOn: []
    jobs:
      - template: /azure-pipeline/jobs/services.yml
        parameters:
          pool: "$(serverPool)"
          solutions:
            - key: service1
              value: "/src/service1.csproj" 

  - stage: Merge
    dependsOn:
      - Server
      - Client
    jobs:
      - template: /azure-pipeline/jobs/merge.yml
        parameters:
          pool: "$(serverPool)"
          artifact: "$(collectionArtifact)"
          serverSitecoreArtifact: "$(serverSitecoreArtifact)"
	   clientArtifact: "$(clientArtifact)"
          mergeArtifact: "$(mergeArtifact)"

Loop door een lijst om verschillende services te bouwen

Wanneer je verder inzoomt op de Services taak. Deze staat hieronder uitgeschreven. De services yaml wordt als template aangeroepen in de ‘Develop CI’ pipeline onder het kopje templates. De uitkomst is dat deze job meerdere artifacts maakt. Een artifact is de uitkomst van een build taak. Dit kan een mapje met dll’s zijn of een mapje met css en javascript.

In de ‘Develop CI’ yaml defineer ik de parameters. In de services yaml staan ook parameters, maar deze worden overschreven. Deze staan er alleen maar voor het geval de parameters niet wordt meegegeven.

Hieronder de yaml pipeline voor /azure-pipeline/jobs/services.yml


parameters:
  pool:
    vmImage: windows-2019
  nuGetVersion: "5.2.0"
  solutionDir: '$(Build.SourcesDirectory)/src/'
  slnFile: '$(Build.SourcesDirectory)/src/Project.sln'
  job: ServicesRabbit
  solutions: 
    - key: service1
      value: "/src/service1.csproj" 
  BuildArguments: "/p:DeployOnBuild=True /p:DeployDefaultTarget=WebPublish /p:WebPublishMethod=FileSystem"

jobs:
  - job: ${{ parameters.job }}
    displayName: "Services "
    workspace:
      clean: all
    pool:
      vmImage: ${{ parameters.pool }}
      demands:
        - msbuild
    variables:
      outputPath: '$(build.artifactstagingdirectory)\is_over_written_default'
    steps:
      - checkout: self
        clean: true
        fetchDepth: 1

      - template: /azure-pipeline/steps/server_net_restore.yml
        parameters:
          solution: ${{ parameters.slnFile }}

      - ${{ each item in parameters.solutions }}:

        - task: PowerShell@2
          enabled: true
          displayName: "[net] update output path"
          inputs:
            targetType: 'inline'
            script: |
              $updateOutputPath = "$(build.artifactstagingdirectory)/${{ item.key }}"
              Write-Output "##vso[task.setvariable variable=outputPath]$updateOutputPath"

        - task: VSBuild@1
          displayName: "[net] Build ${{ item.key }}"
          inputs:
            solution: "$(Build.SourcesDirectory)${{ item.value }}"
            vsVersion: "16.0"
            msbuildArgs: '${{ parameters.BuildArguments }} /p:SolutionDir="${{ parameters.solutionDir }}"'
            platform: "$(buildPlatform)"
            configuration: "$(buildConfiguration)"
            maximumCpuCount: true

        - task: PublishPipelineArtifact@1
          enabled: true
          displayName: "[net] Publish Project ${{ item.key }}"
          inputs:
            artifactName: ${{ item.key }}
            targetPath: $(build.artifactstagingdirectory)/${{ item.key }}
            publishLocation: "pipeline"

Losse stappen binnen de jobs

Om onderdelen her te gebruiken binnen de verschillende jobs, splits je de stappen uit. Dit is bijvoorbeeld handig voor het ophalen van de externe software bibliotheken. Deze stap wordt op meerdere plekken gebruikt. Om dubbele code te voorkomen gebruik deze stap als template.

In het onderstaande voorbeeld restore ik de nuget packages van de solution. Voor dit .NET Framework project wordt de packages.config style gebruikt. In de eerste taak wordt gezocht naar alle ‘packages.config’ bestanden in het project. Hier wordt een hash van gemaakt zodat als er iets in één van deze bestanden iets veranderd er geen gebruik wordt gemaakt van de oude cache map. Als de config bestanden niet veranderen dan blijft de key het zelfde. In Azure Devops is de cache key scoped tot de pipeline en als deze niet binnen 7 dagen wordt gebruikt dan wordt de inhoud verwijderd.

De inhoud van: /azure-pipeline/steps/server_net_restore.yml

parameters:
  verbose: false
  nuGetVersion: "5.2.0"
  solution: '$(Build.SourcesDirectory)\src\Project.sln’

steps:

  - task: Cache@2
    displayName: "[net] Cache NuGet packages"
    inputs:
      key: 'nuget | "$(Agent.OS)" | **/packages.config,!**/bin/**'
      restoreKeys: |
        nuget | "$(Agent.OS)"
      path: '$(Build.SourcesDirectory)/src/packages'

  - task: NuGetToolInstaller@1
    displayName: "[net] Use NuGet ${{ parameters.nuGetVersion }}"
    inputs:
      versionSpec: ${{ parameters.nuGetVersion }}
      
  - task: NuGetCommand@2
    displayName: "[net] NuGet Restore"
    inputs:
      restoreSolution: "${{ parameters.solution }}"
      feedsToUse: config

Merge job

Als laatste stap in het build proces is het combineren van de artifacts voordat deze naar een omgeving worden gereleased. Het is aan jou of je dit in de build stap doet of dat je dit tijdens de release doet. In het onderstaande voorbeeld is het niet te zien maar in mijn geval waren verschillende front-end frameworks die op verschillende locaties staan dus leek het mij slim om dit al voordat de release start alles al bij elkaar te brengen.

Dit is : /azure-pipeline/jobs/merge.yml

parameters:
  pool: "ubuntu-latest"
  serverSitecoreArtifact: "server_sitecore"
  clientArtifact: "client"
  mergeArtifact: "merge"

jobs:
  - job: Merge
    displayName: Merge website artifacts
    pool:
      vmImage: ${{ parameters.pool }}
    steps:
      - checkout: none

      - task: DownloadPipelineArtifact@2
        displayName: "[$(serverSitecoreArtifact)] DownloadPipelineArtifact"
        inputs:
          artifact: $(serverSitecoreArtifact)
          downloadPath: '$(pipeline.workspace)/$(serverSitecoreArtifact)'

      - task: DownloadPipelineArtifact@2
        displayName: "[$(clientArtifact)] DownloadPipelineArtifact"
        inputs:
          artifact: $(clientArtifact)
          downloadPath: '$(pipeline.workspace)/$(clientArtifact)'

      - task: PowerShell@2
        enabled: true
        displayName: rename $(serverSitecoreArtifact) to $(mergeArtifact)
        inputs:
          targetType: 'inline'
          script: |
            Move-Item -Path $(pipeline.workspace)/$(serverSitecoreArtifact) -Destination $(pipeline.workspace)/$(mergeArtifact)

      - task: PowerShell@2
        enabled: true
        displayName: Merge $(clientArtifact) in $(mergeArtifact) (When /assets/client already exist)
        inputs:
          targetType: 'inline'
          script: |
            Get-ChildItem -Path $(pipeline.workspace)/$(clientArtifact) | Copy-Item -Destination $(pipeline.workspace)/$(mergeArtifact)/assets/client -Recurse -Force

      - task: PublishPipelineArtifact@1
        enabled: true
        displayName: Publish $(mergeArtifact) Artifact
        inputs:
          targetPath: "$(pipeline.workspace)/$(mergeArtifact)"
          artifact: $(mergeArtifact)
          publishLocation: "pipeline"

De conclusie dat het goed mogelijk is om pipelines op te splitsen in losse bestanden waardoor het makkelijker wordt om het overzicht te houden. Op deze manier voorkom je dubbele code en automatiseer je de release van de applicatie.

Deze blog verscheen voor het eerst op qdraw.nl

Dit bericht is geschreven door: Dion

Tags:

Gecategoriseerd in:

20 februari 2018 • februari 1, 2021 at 9:37 am Qdraw

Tensorflow objectherkenning en Microsoft .NET Core

Met Tensorflow en Artificial Intelligence het is mogelijk om afbeeldingen realtime te analyseren. In deze blogpost ga ik objecten detecteren in afbeeldingen met Tensorflow en C#. TensorFlow is een open source library voor wiskundige berekeningen. Door gebruik te maken van TensorFlow, het meest populaire deep learning framework, is het mogelijk om dit framework in te zetten voor verschillende doeleinden. In dit voorbeeld gebruik ik Tensorflow in combinatie met .NET Core 2



Hoe werkt Tensorflow samen met Microsoft Technologie Detecteer twee auto’s en een persoon; Detecteer twee auto’s en een persoon

Een Artificial Intelligence project

In Theiacore gebruik ik twee Visual Studio projecten in één solution. Theiacore wordt gebruikt voor het genereren van webpagina’s. Ik heb gebruik het MVC Framework voor het genereren van webpagina’s. ObjectDetect wordt gebruikt voor het Artificial Intelligence gedeelte. Voor Tensorflow zijn er bindings (een koppeling tussen twee frameworks) beschikbaar voor verschillende programmeertalen. Python is de meest populaire binding en hiervoor zijn de meeste online voorbeelden beschikbaar. Voor de programmeertaal C# is er TensorflowSharp, dit is een onofficiële binding die goed werkt voor object detection. In dit voorbeeld focus ik mij op het gebruiken van een Machine learning model voor object herkenning. Dit model is getraind op Microsoft COCO. Deze dataset wordt gebruikt voor het herkennen van objecten in 90 verschillende categorieën.

ObjectDetect-project

Ik gebruik het ObjectDetect-project om door middel van Artificial Intelligence data te verzamelen over de afbeeldingen. In de volgende code snippets zal ik toelichten hoe dit werkt.

Laten we beginnen met een blank canvas. In Tensorflow is dit een leeg TFGraph(). In een TFGraph() worden er twee dingen opgeslagen. Het eerste item is een structuur en het tweede is de graph zelf. Een graph zijn de learnings van het deeplearing framework. In Train een tensorflow gezicht object detectie model ligt ik toe hoe het beste zelf een model kunt trainen en deze kan opslaan als frozen-model. Dit model wordt geladen in de graph. De graph is het geheugen van je computer.

public static ImageHolder GetJsonFormat(string input) {

    var list = new List<string>();

Een Catalog is een geindexeerd bestand waarin de namen van de objecten staan uitgeschreven. Het bestand heet: mscoco_label_map.pbtxt

    _catalog = CatalogUtil.ReadCatalogItems(_catalogPath);
    string modelFile = _modelPath;

    using (var graph = new TFGraph()) {

Het frozen model wordt ingeladen in het geheugen. Ik gebruik TFGraph() om de data en de metadata van het model in het geheugen te laden.

        var model = File.ReadAllBytes(modelFile);
        graph.Import(new TFBuffer(model));

Start een nieuwe sessie om de wiskundige berekeningen uit te voeren. In deze sessie worden de objecten herkenend.

        using (var session = new TFSession(graph)) {

De variable input in een string met de locatie van de afbeelding
Deze wordt geïmporteerd als multidimensionale array, wat ook wel een Tensor wordt genoemd.

            var tensor = ImageUtil.CreateTensorFromImageFile(input, TFDataType.UInt8);

Met de runner wordt het mogelijk om het algoritme te tweaken. Zo is het mogelijk om de inputs, outputs en te configureren.

            var runner = session.GetRunner();
            runner
                .AddInput(graph["image_tensor"][0], tensor)
                .Fetch(
                    graph["detection_boxes"][0],
                    graph["detection_scores"][0],
                    graph["detection_classes"][0],
                    graph["num_detections"][0]);
            var output = runner.Run();

            var boxes = (float[,,])output[0].GetValue(jagged: false);
            var scores = (float[,])output[1].GetValue(jagged: false);
            var classes = (float[,])output[2].GetValue(jagged: false);
            var num = (float[])output[3].GetValue(jagged: false);

De boxes, scores and classes zijn arrays en ik gebruik GetBoxes om de waardes over de objecten uit deze arrays te krijgen.

            var getBoxes = GetBoxes(boxes, scores, classes, input, 0.5);
            return getBoxes;
        }
    }
}

 

Krijg de complete solution werkend.

De broncode van het complete project is publiek beschikbaar, zo kun je exact zien hoe ik het opgelost heb. De volgende stappen ben je nodig om dit project werkend te krijgen. De tekst gaat verder na de afbeelding

 

Whoop, whoop, dit is het resultaat

Installation steps

Tensorflow vereist dat je een x64 runtime gebruikt. De NET Core SDK is standaard x64. Wanneer je een x86 (32-bits) version gebruikt switch s.v.p. eerst naar een 64-bits versie. Als je van plan bent om Azure webapps te gebruiken, deze zijn bij default 32-bits.

 

1. Clone repository

De eerste stap om de repository te downloaden vanuit GitHub.

$ git clone "https://github.com/qdraw/theiacore"

 

2. Download de Pretrained Tensorflow Model en labels

De frozen-models zijn niet ingesloten in de Github repository. Download en plaats deze bestanden in de map: theiacore/ObjectDetect:

En kopieer deze bestanden naar de map: theiacore/ObjectDetect

 

3. Download Reference assemblies

De Libtensorflow is platform afhankelijk en voor het draaien op bijvoorbeeld een Azure is het nodig dat deze assemblies los worden toegevoegd in de map: theiacore/theiacore

Een overzicht van url’s:

 

Azure web apps

Als je van plan bent om dit project binnen een Azure webapp te gebruiken. Azure webapp draaien .Net Core standaard in x86 mode. In de repository is build-for-azure-x64.sh te vinden. Draai het buildscript en de truck is om de bestanden binnen /theiacore/theiacore/theiacore/bin/release/netcoreapp2.0/win-x64/publish te kopieren naar de wwwroot binnen de Azure webapp.

 

4. Start Theiacore

$ cd theiacore/theiacore
$ dotnet run
Now listening on: http://localhost:63884

Mocht de wereld van Computer Vision je interesse hebben gewekt, maar weet je nog niet hoe je dit kunt toepassen en heb je de nodige vragen? Stuur mij dan een mailtje dan kunnen we een kopje koffie drinken.

Dit bericht is geschreven door: Dion

Tags: , , ,

Gecategoriseerd in:

5 februari 2018 • februari 1, 2021 at 9:37 am Qdraw

Met Tabletop bijhouden of de tafelvoetbal vrij is

Realtime bijhouden of een ruimte bezet is, het kan allemaal met Internet of Things. Als side project heb ik een webapp gemaakt waarbij te zien of een ruimte vrij is of niet. Hier maakt ik gebruik van een Arduino met bewegingssensor die waardes doorstuurt naar een website.

Realtime zien of een kamer vrij is.

In één oogopslag is te zien of ruimte bezet is en in de webapp is er te zien wanneer er voor het laatste activiteit is geweest. In de grafiek onder de laatste activiteit is er een overzicht van alle recente bewegingen.



Met Tabletop bijhouden of de tafelvoetbaltafel vrij is Live – Table footbal webapp ; tabletop, mockup, webapp

Fun facts

De tabletop app draait nu vanaf 10 januari op het kantoor waar ik werk. Het gaat over een periode van 16 werkdagen. Dit zijn leuke weetjes: Er zijn drie pieken op een dag. De eerste piek is om 12:30. Je hebt dan 12% kans dat de tafel dan vrij is. Om 13:00 uur is het tot nu toe altijd piektijd en om half 4 is de kans 25%.



Met Tabletop bijhouden of de tafelvoetbaltafel vrij is Live – Table footbal webapp ; Tabletop, all time
Het aantal bewegingen in de Tabletop kamer. Deze zijn samengevoegd per 5 minuten in één staaf.

 

Hoe werkt Tabletop nu?

Een Arduino met een PIR-sensor houdt bij of er beweging wordt geconstateerd. Een Arduino is een microprocessor waarbij het mogelijk is om relatief eenvoudig zelf programma’s op te zetten. Een PIR-sensor wordt ook gebruikt in buitenlampen met een bewegingssensor. Deze stuurt via het internet berichten door naar een webserver. Deze server kan twee type acties uitvoeren. De eerste actie is het verzamelen van data en deze wegschrijven naar een database. De tweede actie is het tonen van data. Wanneer er een bezoeker op de webapp komt wordt een data visualisatie gemaakt van de meest recente bewegingen.

Voor deze setup heb ik gebruik gemaakt van de volgende technieken:

  • .NET Core 2
  • ASP.NET MVC
  • SignalR SSE/Websockets
  • Send events van een Arduino met een ethernet shield en een PIR-Sensor
  • D3.v4 Javascript Datavisualisation

 

Mocht je geïnteresseerd zijn in de werking dan is de code open source beschikbaar en op Github te downloaden. Heb je vragen stuur mij dan een mailtje dan kunnen we een kopje koffiedrinken.



Met Tabletop bijhouden of de tafelvoetbaltafel vrij is Het aantal bewegingen in de Tabletop kamer. Deze zijn samengevoegd per 5 minuten in één staaf. ; Visual Studio, C#, .Net Core 2, MVC

Gebruik SignalR om de UI realtime te updaten

Dit bericht is geschreven door: Dion

Tags: , ,

Gecategoriseerd in: