C#

>C#

API Showcase auf GitHub

Ich habe hier mal ein kleines Repo erstellt um verschiedene APIs zu zeigen.
Wie gewohnt mit fertigem Programmcode. Zusätzlich sind kleine Präsentationen enthalten um einen kleinen Überblick zu geben.

Derzeit sind enthalten:

  • EPLAN Scripting
  • EPLAN API
  • Siemens TIA Portal Openness

Also falls Jemand Interesse hat, einfach bei mir melden :^)

By |2019-03-26T13:38:17+02:002019-03-26|C#, EPLAN, EPLAN-API, TIA Portal|

MuteToHue

Ich hatte ja einen CallMonitor für meine Fritzbox, welcher eine Lampe anschaltet wenn ich telefoniere.
Da wir unsere Telefonanlage nun auf Starface umgestellt haben, musste eine andere Lösung her.

Lange habe ich mir Gedanken gemacht, was hier wohl die beste Lösung ist.
Das Projekt wollte ich unbedingt in .NET Core machen, da ich hier noch nichts gemacht habe.

Da ich nun ausschließlich mit dem Headset telefonieren (dazu noch Skype, WebEx, usw.), läuft das komplette Audio über den Mac.
Wenn ich den Audio-Out mute, weil z.B. Radio läuft, geht das Audio trotzdem am Headset. Das ist sehr angenehm… denn nun musste ich nur noch den Mute-State überprüfen.

Am Mac ist das leider nicht mehr so einfach, aber habe eine Lösung hier gefunden:

public bool GetMuteState()
{
    var readMuteCommand = "osascript -e 'output muted of (get volume settings)'";
    string isMutedString = ReadFromBash(readMuteCommand);
    isMutedString = isMutedString.TrimEnd(Environment.NewLine.ToCharArray()).ToUpper();
    bool newState = isMutedString == "TRUE";
    return newState;
}

public string ReadFromBash(string readMuteCommand)
{
    var escapedArgs = readMuteCommand.Replace("\"", "\\\"");
    var process = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            FileName = "/bin/bash",
            Arguments = $"-c {QUOTE}{escapedArgs}{QUOTE}",
            RedirectStandardOutput = true,
            UseShellExecute = false,
            CreateNoWindow = true,
        }
    };
    process.Start();
    string result = process.StandardOutput.ReadToEnd();
    process.WaitForExit();
    return result;
}

Das überprüfe ich nun alle 5s und wenn sich der Wert ändert, dann wird die Lampe geschalten:

Console.WriteLine("Monitoring mute state...");
bool isMuted = GetMuteState(); // Starting state
while (true)
{
    try
    {
        // Read state
        bool newState = GetMuteState();
        if (isMuted != newState)
        {
            Console.WriteLine("Muted: " + isMuted + " --> " + newState);
            SetHueState(newState);
            isMuted = newState; // All OK, set new state
        }

        Thread.Sleep(SLEEPTIME);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error: " + Environment.NewLine + ex);
    }
}

Aber das Lampen schalten war nicht so einfach…

  • Erster Versuch über Q42.HueApi: Leider stürzt das Programm beim Zugriff auf die Klassen ab
  • Zweiter Versuch über IronPython ein kleine Script zu laden und ausführen: Das Compilieren gestaltet sich bisschen schwierig
  • Lösung: Ich habe ein kleines Python Script mit Platzhalter (True/False) ob die Lampe an ist. Zur Laufzeit ersetzt ich den Wert und führe es über die Bash aus
public void SetHueState(bool newState)
{
    // Read python script and replace placeholder
    var filename = @"/Users/moz/Documents/GitHub/Suplanus.MuteToHue/src/ControlViaPhue.py"; // todo: relative path
    var tempFile = Path.Combine(Path.GetTempPath(), "MuteToHue.py");
    string content = File.ReadAllText(filename, Encoding.UTF8);
    string stateString = FirstCharToUpper(newState.ToString());
    content = content.Replace("$STATE$", stateString);
    File.WriteAllText(tempFile, content, Encoding.UTF8);

    // Execute python script
    var command = $"python {QUOTE}{tempFile}{QUOTE}";
    var result = ReadFromBash(command);
}

public static string FirstCharToUpper(string input)
{
    return input.First().ToString().ToUpper() + input.Substring(1);
}
#!/usr/bin/python

from phue import Bridge
import os
import logging

HUEBRIDGEIP = "192.168.178.79"
LIGHTNAME = "OnAir" # OnAir # Buro

# Init hue
logging.basicConfig()

Bridge = Bridge(HUEBRIDGEIP)
Bridge.connect() # If the app is not registered and the button is not pressed, press the button and call connect() (this only needs to be run a single time)
Bridge.get_api() # Get the bridge state (This returns the full dictionary that you can explore)
light_names = Bridge.get_light_objects('name') # Get a dictionary with the light name as the key
LAMP = light_names[LIGHTNAME] # Get light object
LAMP.on = $STATE$ # True # $STATE$
LAMP.brightness = 254

Das Ganze findet ihr wie gewohnt auf GitHub.

By |2019-03-13T07:22:19+02:002019-02-16|C#, Projekte|

Dokumentation erstellen mit MkDocs

Ich liebe Markdown. Es ist einfach zu schreiben und man bekommt ein schönes Resultat.
Darum habe ich mich entschlossen viele der Dokumentation welche ich schreiben muss darf, auch in Markdown zu schreiben.

Hab mich bei der zweiten Auflage meines Buches dazu entschieden auch eine kleine Website zu machen. WordPress oder ein anderes CMS war für den Anwendungsfall einfach zu groß.

Zufällig bin ich dann mal über MkDocs gestolpert. Dies generiert aus Markdown files schöne Websites die sich super für Dokumentationen eignen. Bekannter dürfte Jekyll sein, aber das ist wieder zu groß für so kleine Dokus.

Da ich auf der Seite die ganzen Beispiel vom Buch darstellen wollte und natürlich zu faul bin das alles zu kopieren… Hab ich mir einfach ein kleines C# Programm erstellt, welches mir die Arbeit abnimmt.
Es generiert Pro Kapitel-Ordner und Script-Datei eine Markdown-Datei mit Überschriften usw.
Zusätzlich wird auch noch die Übersicht generiert.
In der Debug-Konsole lass ich mir auch noch gleich die Menüpunkte ausgeben, welche dann in der YML-Datei von MkDocs landen.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace DocuCreator
{
  class Program
  {
    static void Main(string[] args)
    {
      // Output
      string outputDirectory = @"\\Mac\Home\Documents\GitHub\EplanElectricP8Automatisieren.V2.Writing\Documentation\Website\docs\scripts";
      if (Directory.Exists(outputDirectory))
      {
        Directory.Delete(outputDirectory, true);
      }
      Directory.CreateDirectory(outputDirectory);

      // Overview
      Console.WriteLine("- 'Übersicht': 'scripts/Overview.md'");
      string fileNameOVerview = Path.Combine(outputDirectory, "Overview.md");
      StringBuilder sbOverview = new StringBuilder();
      sbOverview.AppendLine(); // Needed for Encoding problem
      sbOverview.AppendLine("**Übersicht**");
      sbOverview.AppendLine();
      sbOverview.AppendLine("---"); // Devider line
      sbOverview.AppendLine();

      // Folder
      string path = @"\\Mac\Home\Documents\GitHub\EplanElectricP8Automatisieren.V2.Writing\Data\Code\EPLAN Scripting Project";
      var directories = Directory.GetDirectories(path, "*_*").OrderBy(obj => obj).ToList();
      foreach (var directory in directories)
      {
        string directoryName = new DirectoryInfo(directory).Name;
        string directoryNameWithSpaces = directoryName.Replace("_", " ");
        var directoryNameWithoutUmlauts = directoryName
             .Replace("ä", "ae")
             .Replace("ö", "oe")
             .Replace("ü", "ue")
          ;

        // Overview
        sbOverview.AppendLine($"### [{directoryNameWithSpaces}]({directoryNameWithoutUmlauts}.md)");

        // Files
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(); // Needed for Encoding problem        
        string header1 = $"**{directoryNameWithSpaces}**"; // Main header
        sb.AppendLine(header1);
        sb.AppendLine();
        sb.AppendLine("---"); // Devider line
        sb.AppendLine();

        var files = Directory.GetFiles(directory, "*.cs", SearchOption.TopDirectoryOnly).OrderBy(obj => obj).ToList();
        foreach (var file in files)
        {
          AddFile(file, sbOverview, directoryNameWithoutUmlauts, sb, files);
        }

        // Write file
        string outputFile = Path.Combine(outputDirectory, directoryNameWithoutUmlauts + ".md");
        File.WriteAllText(outputFile, sb.ToString(), Encoding.UTF8);
        Console.WriteLine($"- '{directoryNameWithSpaces}': 'scripts/{directoryNameWithoutUmlauts}.md'");

      }

      // RemoteClient
      Console.WriteLine("- 'RemoteClient': 'scripts/RemoteClient.md'");
      AddFileRemoteClient(sbOverview, outputDirectory);

      // Write Overview
      File.WriteAllText(fileNameOVerview, sbOverview.ToString(), Encoding.UTF8);
    }

    private static void AddFileRemoteClient(StringBuilder sbOverview, string outputDirectory)
    {
      var fileRemoteClient = @"\\Mac\Home\Documents\GitHub\EplanElectricP8Automatisieren.V2.Writing\Data\Code\EPLAN Remote Client\Program.cs";
      string outputFile = Path.Combine(outputDirectory, "RemoteClient.md");

      sbOverview.AppendLine($"### [RemoteClient](RemoteClient.md)");

      StringBuilder sb = new StringBuilder();
      string header1 = "##### RemoteClient"; // Main header
      sb.AppendLine(header1);
      string lines = File.ReadAllText(fileRemoteClient);
      sb.AppendLine("```csharp"); // Code start
      sb.AppendLine(lines); // Code
      sb.AppendLine("```"); // Code end
      sb.AppendLine();
      File.WriteAllText(outputFile, sb.ToString(), Encoding.UTF8);
    }

    private static void AddFile(string file, StringBuilder sbOverview, string directoryNameWithoutUmlauts, StringBuilder sb,
      List<string> files)
    {
      string fileName = Path.GetFileNameWithoutExtension(file);
      string fileNameWithSpaces = fileName.Replace("_", " ");

      // ReSharper disable once PossibleNullReferenceException
      var anker = fileNameWithSpaces
          .ToLower()
          .Replace(" ", "-")
          .Replace('ä', 'a')
          .Replace('ö', 'o')
          .Replace('ü', 'u')
        ;
      sbOverview.AppendLine($"- [{fileNameWithSpaces}]({directoryNameWithoutUmlauts}.md#{anker})");

      string header2 = $"##### {fileNameWithSpaces}"; // Sub header
      sb.AppendLine(header2);
      string lines = File.ReadAllText(file);
      sb.AppendLine("```csharp"); // Code start
      sb.AppendLine(lines); // Code
      sb.AppendLine("```"); // Code end
      sb.AppendLine();

      // Devider line if not last
      if (!files.Last().Equals(file))
      {
        sb.AppendLine();
        sb.AppendLine("<br>");
      }
    }
  }
}

Alles bisl Quick-And-Dirty, aber läuft super.

Vorteil: Bei jeder Änderung am Script muss ich nur das kurz laufen lassen und die Website ist wieder aktuell. Für künftige Auflagen eine große Erleichterung.

By |2018-08-21T08:32:53+02:002018-08-21|C#|

VSColorOutput

Mich hat der monotone Output in Visual Studio immer schon gestört. Viele Informationen die (meist) nicht interessieren und einige sehr wichtige die man vor lauter Bäumen nicht sieht.

Durch Zufall, ohne danach zu suchen, bin ich auf VSColorOutput gestoßen und bin begeistert. Echte Empfehlung!

screenshot

By |2018-04-24T16:13:04+02:002016-10-14|C#, Coding|

Logging-Framework: Serilog

Ich werde ab und zu gefragt wie ich denn Daten zur Laufzeit (nicht Debugging) mit logge.

Log4Net war hier lange Platzhirsch, aber für mich und viele andere ist es nun Serilog. Denkbar einfach zu implementieren. Es gibt viele Sinks (Ziele für das Logging, z.B. Datenbanken, Dateien, Console).

Das ganze in kostenlos und OpenSource…

Hier ein kleines Beispiel für einen Logger der in eine Datei schreibt und zusätzlich in die Console, auch schön in Bunt von hier:

Log.Logger = new LoggerConfiguration()
    .WriteTo.LiterateConsole()
    .WriteTo.RollingFile("log-{Date}.txt")
    .CreateLogger();

Log.Information("Ah, there you are!");

coloredconsolesinkdemo

By |2016-10-13T13:46:45+02:002016-10-13|C#|