Montag, 31. Oktober 2022

Tool Lagerverwaltung (Teil 4) Datenbank und Schnittstelle

Bisher hatte ich nur erwähnt, dass als dritte Schicht die Datenbank abbildet. Klingt etwas übertrieben, macht aber einen deutlichen Performance unterschied gegenüber der Verwendung von Text Dateien in CSV Format.

 

Benötigt

  • Visual Studio 2022 oder anderen Compiler
  • .NET 6.0
  • Codexzier's Application Framework
  • GitHub
  • SQLite
  • MS SQL oder PostgreSQL (nicht zwingend für den weiteren Verlauf)

Einbinden

Das Einbinden einer Datenbank soll so entkoppelt sein, dass die Datenbank Auswechselbar sein soll. Spricht, entweder soll SQLite für den Lokalen Einsatz verwendbar sein oder eine Externe Datenbank auf einem anderen Server.

 

Kein OR-Mapper

Man könnte an der stelle einen OR-Mapper verwenden, der für die Größe des Projektes auch völlig ausreicht. Aber ich will auf diesen Komfort verzichten und schreibe die SQL-Statement aus. Das Thema möchte ich für einen anderen Blogeintrag nutzen, in dem ein Umzug zum OR-Mapper beschrieben wird.

 

Tabellen

In den vorigen Post habe ich von den Einträgen immer von einer Sache gesprochen. Das liegt daran, dass in Zukunft zu speicherndem Inhalt offen ist und durch den Benutzer selbst definiert werden kann durch Vorlagen. 

Nun soll die Haupttabelle nicht 'Sache' oder 'Thing' heißen, und 'Objekt' erst recht nicht. Passend ist 'Artikel' oder 'Item'. Fehlt also noch als Eigenschaft ID für die Eindeutigkeit des Datensatzes und 'Titel' soll in Kurzform beschreiben, was der Eintrag ist. Weitere Eigenschaften kommen später, denn jede weitere Eigenschaft muss sich aus dem Nutzerkontext ergeben.

 public class ArticleItem  
 {  
   public long Id { get; set; }  
   public string Title { get; set; } = "title";  
   public string Description { get; set; } = String.Empty;  
   public bool IsArchived { get; set; } = false;  
   public bool IsTemplate { get; set; }  
   IEnumerable<IArticleSubItem> ArticleSubItems { get; set; } = Array.Empty<IArticleSubItem>();  
 }  


Schnittstellen definieren

Grundlegend werden mindestens drei Methoden benötigt. Speichern, Abrufen und aktualisieren. An der Stelle muss ich noch betonen, dass noch kein komplettes Konzept steht, weshalb die Schnittstelle rudimentär bleibt.

 public interface IDatabaseConnector  
 {  
   void CreateTable<TTable>();  
   void Insert(ArticleItem articleItem);  
   IEnumerable<ArticleItem> GetAll();  
   ArticleItem GetById(long id);  
   IArticleSubItem SubItem_GetById(long id);  
   void Update(ArticleItem articleItem);  
   void Update(IArticleSubItem subItem);  
 } 

 

Nuget Pakete für SQLite

Hier reicht nur die Pakete zu installieren für SQLite. Die Installation MS SQL oder PostgreSql gehe ich nur ansatzweise an, um später zu zeigen, inwieweit ein Wechsel zu einer anderen Datenbank möglich ist. Deshalb werden bei den beiden Datenbank Technologien, dessen Nuget Pakete nicht installiert.

  • SQLite-net-standard 1.5.1
  • PostgreSQL
  • MS SQL

PoC - Proof of Concept

Der Wechsel zu einer anderen Datenbank. Die Funktionalitäten würde man zunächst in der Solution unter dem Projekt Komponenten hinterlegen. Das würde bedeuten, dass die Abhängigkeiten zu den drei Datenbanken in diesem Projekt liegen und später vorrausetzen, dass die Bibliotheken zu den anderen zwei immer gefordert werden, obwohl nur eine der möglichen Datenbank verwendet wird.

 

-> Option 1

Alles so lassen und später die Pakete rausnehmen, die man nicht braucht.

 

-> Option 2

Schnittstelle und Datenbanken in eigene Projekte verschieben. Über das Projekt ‚Componente' wird je nach Einstellung entschieden, welche Datenbank und Bibliotheken geladen werden soll.

 

Qual der Wahl

Bei einem Hobby Projekt reicht in der Regel die erste Option aus, solange man das für sich nur nutzen möchte. Die Anwendung hat keine großen Performance Ansprüche und wenn Probleme auftreten, kann man diese selbst schnell lösen.

Warum Option 2? Nicht wegen der Performance, sondern die Möglichkeit statt einer Lokalen Datenbank, auf eine zentrale Datenbank zu wechseln. Aber wenn diese Option nicht genutzt wird, sollten zumindest keine Probleme entstehen mit Referenzen, die nicht genutzt werden.

 

Erweiterung der Solution

Ein Projekt muss angelegt werden für die Datenobjekte und Schnittstelle, die ich hier 'SharedBasis'. Und für jede zu verwendeter Datenbank bekommt ein eigenes Projekt und erhält die Referenz zu dem Projekt 'SharedBasis'.



Was kommt in 'SharedBasis'

Hauptsächlich die Grunddaten Objekte und Schnittstellen, mehr sollte da nicht hineinkommen. Im Folgenden belasse ich mich auf die Schnittstelle für das Abrufen und Aktualisieren der Daten. Das Datenobjekt Artikel bekommt nur wesentliche Informationen, erst über dem ArticleSubItem wird der Umfang des Artikels ausgebaut. Darauf gehe ich in einen anderen Blogeintrag weiter darauf ein.

 public interface IArticleSubItem  
 {  
   long Id { get; set; }  
   long ArticleId { get; set; }  
 }  

Für den Ausbau der zu definierenden 'SubItems', ist für den aktuellen Stand noch nicht erforderlich. Letzten Endes gehe ich nur auf Grundlagen ein, die aber soweit reichen müssen, damit sich für mich die weiteren Details erschließen.

 

Ein Projekt pro Datenbank

Die Verbindung zu den Datenbanken sind eigentlich bei allen sehr ähnlich und doch werden diese getrennt behandelt in jeweils einem Projekt. In jedes Datenbank Projekt muss einmal die Reference 'WarehouseManagement.SharedBasis' hinzugefügt werden. Für ein wenig Ordnung habe ich die Projekte in einen Solution Ordner 'Data' angelegt.


Interface einsetzen

Wie bereits im Projekt 'WarehouseManagement.DatabaseSQLite' zu sehen ist, habe ich die Klasse DatabaseConnector.cs angelegt, bzw. wurden die vorhandenen Klassen mit den Namen 'Class.cs' umbenannt und das Interface 'IDatabaseConnector' implementiert.

 public class DatabaseConnector : IDatabaseConnector  
 {  
   #region interface methods  
   public void CreateTable<TTable>()  
   {  
     this.Execute(db => db.CreateTable<TTable>());  
   }  
   public IEnumerable<ArticleItem> GetAll()  
   {  
     throw new NotImplementedException();  
   }  
   public ArticleItem GetById(long id)  
   {  
     throw new NotImplementedException();  
   }  
   public void Insert(ArticleItem articleItem)  
   {  
     throw new NotImplementedException();  
   }  
   public IArticleSubItem SubItem_GetById(long id)  
   {  
     throw new NotImplementedException();  
   }  
   public void Update(ArticleItem articleItem)  
   {  
     throw new NotImplementedException();  
   }  
   public void Update(IArticleSubItem subItem)  
   {  
     throw new NotImplementedException();  
   }  
   #endregion  
 }   

Attribute einsetzen

Man kann den Namen der Datenbank Technologie in der Eigenschaft weglegen, erfordert jedoch, dass eine Instance vom 'DatabaseConnector' gestartet wird. Oder erweitert den Klassennamen zu 'DatabaseConnector' mit den Technologienamen. Beides ist nicht schön und deshalb passt die Verwendung von selbst erstellten Attributen, um dort den Technologienamen abzulegen.

 public class DatabaseConnectorNameAttribute : Attribute  
 {  
   public DatabaseConnectorNameAttribute(string name)  
   {  
     this.Name = name;  
   }  
   public string Name { get; }  
 }  

So bleibt der Grundname und dennoch können die Klassen von ihrer Technologie unterschieden werden.

 [DatabaseConnectorName("SQLite")]  
 public class DatabaseConnector : IDatabaseConnector  
 {  
 ...  
 [DatabaseConnectorName("PostgreSQL")]  
 public class DatabaseConnector : IDatabaseConnector  
 {  
 ...  
 [DatabaseConnectorName("MS SQL")]  
 public class DatabaseConnector : IDatabaseConnector  
 {  
 ...  

Test anlegen

Für das Erfassen der Datenbank Verbindungen (DatabaseConnector) wird im Projekt 'WarehouseManagement.Components' keine Reference der Datenbank Projekte hinzugefügt. Stattdessen soll das zur Laufzeit passieren und hierfür soll eine Klasse die Aufgabe übernehmen. Hier muss jedoch die 'WarehouseManagement.SharedBasis' Reference hinzugefügt werden, um später die Schnittstelle aus den Datenbank Projekten zu erkennen.

Der erste Unit Test soll die kompilierten DLLs aus den Datenbank Projekten einlesen. Beim ersten Mal und nach Änderungen der Datenprojekten, muss darauf geachtet werden, dass diese einen Aktuellen Bild hinterlegen. Oder einfach die gesamte Solution Inhalt neu kompilieren.

 [TestClass]  
 public class DatabaseConnectionTest  
 {  
   [TestMethod]  
   public void ReadDlls()  
   {  
     // arrange  
     // act  
     var result = DatabaseConnection.GetDatabaseConnectors();  
     // assert  
     Assert.IsNotNull(result);  
     Assert.AreEqual(3, result.Count());  
   }  
 }  

DLL Dateien Namentlich filtern

Da alle Projektinhalte in einem Ordner landen, werden beim Abrufen der DLL-Dateinamen auch die mitgenommen, die keine Verbindung haben zu einer Datenbank.

 

 

Deshalb sollte hier der einfach nach den Projektnamen 'WarehouseManagement.Database' gefiltert werden.

Nun läuft der Test durch, ohne dass die DLLs gefunden wurden. Das liegt daran, dass für das Unit Test Projekt die DLLs nicht als Referenz genannt sind. Deshalb müssen diese noch rüber kopiert werden aus den Bin Order des jeweiligen Datenbank Projektes. Damit man über den Dateiexplorer nicht immer händisch rüber kopieren muss, kann dies über den Test Projekt Eigenschaften angeordnet werden mit den Build -> Output -> Post-build event und Xcopy

xcopy $(SolutionDir)WarehouseManagement.DatabaseSQLite\bin\Debug\net6.0\WarehouseManagement.DatabaseSQLite.dll $(SolutionDir)WarehouseManagement.Components.Test\bin\Debug\net6.0 /Y  
 xcopy $(SolutionDir)WarehouseManagement.DatabaseMsSQL\bin\Debug\net6.0\WarehouseManagement.DatabaseMsSQL.dll $(SolutionDir)WarehouseManagement.Components.Test\bin\Debug\net6.0 /Y  
 xcopy $(SolutionDir)WarehouseManagement.DatabasePostgreSQL\bin\Debug\net6.0\WarehouseManagement.DatabasePostgreSQL.dll $(SolutionDir)WarehouseManagement.Components.Test\bin\Debug\net6.0 /Y  

Methode zu Ende ausschreiben

Nun sollte die Methode zum Einlesen der Datenbank Verbindungsstücke aus den DLLs gelesen werden können. Die Folgende Ausführung ist sehr kompakt geschrieben, welches durch Linq, Lamda-Ausdrücken und Reflection realisiert wird. Hier empfehle ich, die von euch gewohnte Ausschreibung des Codes zu verwenden. Dann fällt ein späterer Einblick in die einzelnen Codestellen leichter.

 public class DatabaseConnection  
 {  
   public static IEnumerable<string> GetDatabaseConnectors()  
   {  
     return Directory  
       .GetFiles(Environment.CurrentDirectory)  
       .Where(file => file.Contains("WarehouseManagement.Database"))  
       .Select(Assembly.LoadFrom)  
       .SelectMany(assembly => assembly.GetExportedTypes())  
       .Where(type => typeof(IDatabaseConnector).IsAssignableFrom(type))  
       .Select(type => type.Name);  
   }  
 }  

Ansatz fertig

Und lasst euch in euerer Programmierung nicht reinreden, wenn eure Lösung funktioniert. Ein besser gibt's nicht. Sondern nur andere Lösungsformen.

Außerhalb des Themas 'Tool Lagerverwaltung' beschreibe ich den weiteren Ausbau der Datenbank Verbindungen in einen für sich geschlossenen Blogpost. In diesen ist der Ansatz gezeigt, wie sowas aufgebaut sein könnte.


Codestand: Database and interface

 

Übersicht

Teil 1 - Wiki anlegen in GitHub

Teil 2 - Neue Solution

Teil 3 - Noch ein Wiki anlegen

Teil 4 - Datenbank und Schnittstelle

Teil 5 - Konzept ausschreiben

Teil 6 - Umzug nach .NET MAUI

 

Links

 

Mittwoch, 31. August 2022

Tool Lagerverwaltung (Teil 3) Noch eine Wiki anlegen

Diesmal ein kurzer Post zur Vollständigkeit für die weitere Entwicklung. Nun braucht die Anwendung selbst eine eigene Wiki. Anders als bei dem Application Framework, wird die Funktionsweise und Benutzung beschrieben.

 

Benötigt

GitHub

 

Begin mit dem Benutzerhandbuch

Jede erstellte Seite soll eine Beschreibung und mindestens eine Skizze haben. Die Beschreibungen sind zunächst als Grundlage, was die Anwendung alles haben und wie sie funktionieren soll. Hier gehören keine Technischen Hintergründe, denn diese gehören entweder im Code als Kommentar oder im Ticket.

 

Am Anfang ein Konzept

Bevor die Anwendung geschrieben wird, soll mit dem Konzepthandbuch die Nutzeranforderungen ermittelt werden. Wenn später die Inhalte fertig sind, können nach und nach die Konzeptbeschreibung zur Benutzerbeschreibung umgestellt werden.

Nun könnte man sagen, dass Konzept kann man sich sparen und man erstelle die Dokumentation, wenn die Anwendung fertig ist. Wenn eine Planung vorliegt, dann ist das Ziel bekannt. Darauf los zu programmieren, führt eher dazu, dass man sein Ziel vergisst.

Die Konzeptbeschreibung ist auch die Möglichkeit die Nutzeranforderung zu spezifizieren und somit im weiteren daraus die Technischen Anforderungen zu formulieren.

Eigentlich eine einfache Sache und dennoch glauben viele, dass man diesen Schritt sich sparen kann. Doch sparen bedeutet am Ende mehr versteckter Zeitaufwand.

 

Wieder mal das GitHub Wiki

Diesmal wird keine Anleitung hinterlegt, wie die Einzelnen Steuerelemente verwendet werden. Wie bereits im vorigen Absatz, werden soll Beschreibungen angelegt. Damit Bildet sich dann Praktisch eine Vorläufige grobe Anforderung, die verfeinert werden kann.

 

Die Inhaltsangabe dient hier zunächst als grobe Richtung. (Im ersten Teil, ist das Beispiel wie das Menü im Edit aussieht)

 * Was kann die Anwendung  
 * Einleitung  
     * Hintergrund  
 * Start Seite  
     * Grundrahmen  
     * Übersichtsliste  
     * Suche  
 * Vorlage Einstellen  
     * Basisfelder  
     * Optionale Felder  
 * Neue Sache  
     * Vorlage verwenden  
     * Eigene Vorlage verwenden  
 * Sache bearbeiten  
     * Optionale Felder hinzufügen  
 * Sache archivieren  
     * Sache wiederherstellen  

Inhalt füllen

Im Ersten Teil habe ich bereits beschrieben, was man bereits mit dem Wiki erzeugen kann und deshalb geht es hier einen Schritt weiter voran mit dem erzeugen der Seite für die 'Start Seite' der Anwendung. Auf der neuen Seite, kommt zu der Textform auch Bilder. Mit diesen wenigen Details, lassen sich im Anschluss bereits die ersten Technischen Anforderungen ableiten.

 

 

Bisher noch nicht erwähnt, hatte ich das Tool Pencil. Neben Ablauf Diagrammen, können Skizzenhaft die Programmoberfläche Gestaltet werden.

 

Nächster Ansatz

Damit ist der nächste Ansatz getan. Aber bevor ich mit den weiteren Teilen der Anwendung weiter schreibe, geht es mit einer Technische Komponente weiter.

 

Übersicht

Teil 1 - Wiki anlegen in GitHub

Teil 2 - Neue Solution

Teil 3 - Noch eine Wiki anlegen

Teil 4 - Datenbank und Schnittstelle

Teil 5 - Konzept ausschreiben

Teil 6 - Umzug nach .NET MAUI

Montag, 22. August 2022

Tool Lagerverwaltung (Teil 2) Neue Solution


Welche Architektur oder welche Form soll das Projekt haben. Am liebsten setze ich auf die Drei-Schichten-Architektur. Klinkt abstrakt und hochtrabend, aber kompliziert ist die Sache nicht. Sich dran halten ist anfangs schwierig. Vorzugsweise setze ich auf Desktop Anwendung.

Benötigt

  • Visual Studio 2022 oder anderen Compiler
  • .NET 6.0
  • Codexzier's Application Framework
  • GitHub

 

Ziel für diesen Blog-Eintrag

Eine neue Solution und die benötigten Projekte einrichten mit dem Ansatz der Drei-Schichten Architektur..

 

Drei-Schichten

Wenn ich Rückblicke, in welchen Formen ein Drei-Schichten Modell aussieht, dann waren diese immer unterschiedlich gestaltet und hielten dennoch erkennbar Drei Schichten. Grundlegen haben wir das Frontend oder auch Benutzeroberfläche genannt, dann die Service-Schicht in der die Daten verarbeitet werden und als drittes die Datenhaltung in einer Datenbank.

 

Solution anlegen

Aber bevor das Projekt angelegt wird, soll eine leere Solution erstellt werden. Idealerweise gibt ihr in die Suche 'Blank' ein.

Nach der Auswahl kommen wir zur Eingabe des Solution Name und das Festlegen des Speicherortes.


Git Repository

Bevor das WPF Projekt Eingesetzt wird, gehört die Pflege der Sourcecodes in das Git System. Also fehlt der Klick auf den Button 'Create Git Repository…'.

Falls die Anmeldung von GitHub noch nicht geschehen ist, dann wird dies jetzt gefordert. In meinem Fall ist das Projekt öffentlich, weshalb der Haken für 'Private repository' raus ist.

Ist das Repository erstellt, dann sollte der aktuelle Stand bereits Online in eurem GitHub Repositories zu finde sein.

 

Einrichten der Projekte

Als erstes kommt das WPF Projekt, das die Frontend Schicht abbildet. Hier verwende ich das Projekt Template, dass bereits die Referenzen zu meinem Application Framework enthält.

Im folgenden kommen zwei Möglichkeiten, um das Template in Visual Studio 2022 einzubinden.

 

Option 1

Die Zip-Datei Codexzier Application Framework Vorlage Augsut 2022 in den Ordner ../Visual Studio 2022/Templates/ProjectTemplates einfügen (zum Download)

 

Option 2

Das Codexzier Application Framework von meinem GitHub Repository herunterladen und in Visual Studio öffnen. Dann oben auf Projekt

Dann das Projekt Template 'Codexzier.Wpf.ApplicationTemplate' auswählen..

..und im nächsten Schritt einen Namen vergeben.

Wählt nun die Solution aus und fügt diesem ein neues Projekt zu.

Das WPF Vorlagen Projekt kann bei Bedarf über die Suche gefunden werden.

Für die Benutzer Oberfläche, solltet ihr für den Namensraum den Bereich Namentlich erkenntlich Beschreiben.


Service Schicht

Das nächste Projekt soll alle Service Komponenten enthalten, die nichts mit der Benutzer Oberfläche zu tun haben. Obwohl ich von Service Schicht gesprochen habe, wird eine einfachen Klassen Bibliothek angelegt. Hier darauf achten, dass ihr die Vorlage für das aktuelle .NET Framework verwendet.

Und wieder einen passen Namen vergeben für die Service Komponenten Schicht.

Im nächsten Schritt muss noch die .NET Version angegeben werden und dann kann das Projekt erstellt werden.


Ein Projekt noch

Eins fehlt noch und zwar das für die Automatischen Tests. Für die UI kann eigentlich auch ein Test Projekt angelegt werden, aber aktuell reicht ein Unit Test Projekt für die Service Komponenten Schicht.

Der Namespace bekommt den Zusatz 'Test'. Damit sollte das Unit Test Projekt direkt unter dem zu testenden Projekt sein.

Zum Schluss wieder die .NET Version auswählen und erstellen.


Initialen Stand sichern

Hier braucht der Text schlichtweg aussagen, dass grundlegend die Projekte angelegt wurden.


Grundlage für das gesamt Projekt

Der zweite Schritt ist erledigt. Eine neue Solution mit den Grundlegenden Projekten ist angelegt. Die Inhalte habe ich nicht zu stark unterteilt, schließlich handelt es sich hier um ein sehr kleines Privat Projekt. Im nächsten Schritt geht’s dann mit dem Konzept und Funktionsumfang weiter.


Übersicht

Dienstag, 2. August 2022

Tool Lagerverwaltung (Teil 1) Wiki anlegen in GitHub

Für das Tool soll das eigene Framework verwendet werden, dass ich in den letzten Jahren immer weiter ausgebaut hatte. Jedoch ist noch offen, eine Dokumentation anzulegen, die den Funktionsumfang sowie Verwendung des Applikation Framework zeigt.

 

Benötigt

  • Visual Studio 2022 oder anderen Compiler
  • .NET 6.0
  • Codexzier's Application Framework
  • GitHub

 

Was ist mein Ziel?

Primär soll ein einfaches Tool entwickelt werden, dass zur Lagerverwaltung verwendet werden kann. Mit der der Entwicklung möchte ich beschreiben, welche Vorgehensweisen und Lösungen ich verwende. Zudem soll zu jeder Funktion aus dem Applikation Framework dokumentiert werden, das ich für den aktuellen Schritt verwendet wird.

 

Neben Produkt

Immer wenn ich eine neue Anwendung geschrieben habe, benötigte ich Grundlegende Inhalte für eine WPF Anwendung mit bestimmten Funktionen die ich selbst mal geschrieben habe. Und weil ich meine Anwendung auch ein Bestimmtes Aussehen haben sollen, habe ich auch die Styles immer mit kopiert. Damit ergaben sich Vorteile aber auch Nachteile.

 

Vorteile

  • Auf den Bisherigen Lösungen etwas Besseres oder neues entwickeln
  • Übung, Übung, Übung

Nachteil

  • Pflege älterer Anwendung erschwert
  • Doppelte Arbeit

 

Ok, gehen wir von der selbst Erklärung rüber zum ersten Schritt.

 

Henne-Ei-Problem

Fange ich mit der Dokumentation an, was funktionieren soll oder fange ich mit dem Programmieren an und dokumentiere? Die Frage, ob man mit der Dokumentation anfängt, ist davon abhängig, um was für eine Dokumentation angelegt werden soll. Bevor etwas entsteht, kann im Grunde nur ein Konzept- oder ein Grundbeschreibungen zu einer Anwendung angelegt werden.

In meinem Fall liegt der Programmcode vor und die Dokumentationsbeschreibung fehlt. Also wie in vielen Projekten.

 

GitHub Wiki

Die Sourcen zu dem Applikation Framework von mir habe ich bereits auf GitHub hochgeladen und dort kann zusätzlich ein Wiki gepflegt werden. Und damit soll's neben dem Hauptprojekt beginnen.

 

Als erstes hilft eine Grobe Inhaltsangabe anzulegen, so dass eine Grundlage vorliegt, an der man sich orientieren kann.

 Home  
 Lizenz  
 Grundlagen  
      Vorlage  
      Neues Projekt einrichten  
      Grundaufbau  
      Weiter Einstellungen  
 Components  
      Event Bus Manager  
      User Settings  
      Animation Helper  
 Styles  
      Blue Gray  
      White Gray  
      Gray White  
 Steuerelemente  
      Button  
      Diagram  
      Folder Browser  
 Game Tree 

So sieht der Text in der Bearbeitung aus in der GitHub Wiki (Zuvor hatte ich am Ende jedem Eintrag immer Page stehen gehabt, leider ist das dann auch der Name der Seite und habe das deshalb abgeändert)

 * [[Lizenz|Lizenz]]  
 * [[Grundlagen|Grundlagen]]  
 > - [[Vorlage|Grundlagen - Vorlage]]  
 > - [[Neues Projekt einrichten|Grundlagen - Neues Projekt einrichten]]  
 > - [[Grundaufbau|Grundlagen Grundaufbau]]  
 > - [[Weiter Einstellungen|Grundlagen - Weitre Einstellungen]]  
 - [[Components|Components]]  
 > - [[Event Bus Manager|Components - Event Bus Manager]]  
 > - [[User Settings|Components - User Settings]]  
 > - [[Animation Helper|Components - Animation Helper]]  
 * [[Styles|Styles]]  
 > - [[Blue Gray|Styles - Blue Gray]]  
 > - [[White Gray|Styles - White Gray]]  
 > - [[Gray White|Styles - Gray White]]  
 * [[Steuerelemente|Steuerelemente]]  
 > - [[Button|Steuerelemente - Button]]  
 > - [[Diagram|Steuerelemente - Diagram]]  
 > - [[Folder Browser|Steuerelemente - Folder Browser]]  
 > - [[Game Tree|Steuerelemente - Game Tree]]  

Und so sieht's dann nach dem Speichern aus.



Gefärbter Text
Im Bild ist zu sehen, dass nur der Text 'Lizenz' in blauer schrift hinterlegt ist. Wenn du suggestiv einen Link vermutest, dann liegst du richtig. Für die Seite Lizenz, habe ich bereits eine Seite angelegt. Alle die noch rot eingefärbten sind, müssen noch erstellt werden. Wenn man auf eines der rot gefärbten Text klickt, kommt statt einem Fehler eine Bearbeitungsmaske, um diese Seite zu erstellen.


 

Grundlage

Der erste Schritt ist damit getan. Nun muss man nur dranbleiben und hin und wieder schauen, ob die Struktur weiterhin passt.


Links

https://docs.github.com/en/communities/documenting-your-project-with-wikis/editing-wiki-content

 

Montag, 18. Juli 2022

AdSense Werbung funktioniert nicht

In den letzten zwei Monaten wurde auf meinem Blog keine Werbung angezeigt. Das war nicht schlimm, da die Einnahme wirklich sehr gering sind und ich mich schon freue, wenn wenigstens 50 Cent im Monat eingehen.

Zunächst dachte ich, das mit der Umstellung, die Formate der AdSense Werbe Banner neu eingestellt werden müssen und das man einen Tag warten muss, bis die Änderung sichtbar wären. Mein Gedanken Gang führte dazu, dass ich später nicht mehr daran gedacht hatte und erst im Urlaub mich dem Problem annahm. 

 

Dienste

  • Blog von blogger.com
  • Google AdSense

 

Ursache des Problems

Mit den Änderungen des Layouts, wurde im Anschluss, auf meinem Blog anschließend keine Werbung angezeigt. Nur in der Vorschau eines Blog-Eintrages wurden die Werbebanner angezeigt.

 

Lösung

Im Menü zum Blog, im Bereich Einnahmen muss die Verknüpfung entfernt und neu verknüpft werden.


Button Adsense entfernen klicken

Nach dem Entfernen sollten sich die Optionen auf der Seite Einnahmen sich verändern und der Button AdSense Verknüpfen sichtbar werden. Nach dem Click ist die neu Verknüpfung abgeschlossen und die Werbung wieder funktionieren.

 

Warum eigentlich

Eigentlich bringt die Menge an Werbung auf meinem Blog keinen Finanziellen Gewinn. In vielen Monaten hatte ich oft Einnahmen unter 10 Cent und da liegt der Gedanke nahe, dass gleich zu lassen.

Aber mich interessiert die Funktion und Verwendung von AdSense. Hier beobachte ich gerne, wie z.B. nach einem Blog-Eintrag in Abhängigkeit mit einem aktuellen Thema, sich die Einnahmen verändern. Das brachte mich auf die Idee, die Beobachtung aufzuschreiben.

 

Sonntag, 15. Mai 2022

Windows 11 und langsame VirtualBox


Im Wandel von Updates kommt mit der Zeit auch ein neues Windows. Und leider funktioniert nicht immer alles. Sehen wir davon ab, das VirtualBox weitgehend Kostenlos ist und deshalb nicht gewährleistet wird, dass alle Umstände funktionieren oder unterstützt werden.

 

Verwendet

  • Windows 11 (oder Windows 10)
  • VirtualBox
  • Virtualisiertes System mit Windows 2000 Professional

 

Problem Beschreibung

Nach einem größeren Update oder mit dem Wechsel von Windows 10 auf Windows 11, kann das Ausführen von Virtuell Box sehr langsam werden. Man kann praktisch sehen wie Pixel für Pixel das Bild aufgebaut wird.

 

Ursache

Nun im Grunde kenne ich nicht die eigentliche Technische Ursache, warum ein eingeschaltetes Feature in Windows 11 für Virtuelle Maschinen Probleme erzeugt. Vielleicht hilft diese Beschreibung, die ich in den Weiten des Internets gefunden habe. (https://www.pctipp.ch/praxis/virtualisierung/quaelend-langsame-virtualbox-unter-windows-11-2742835.html?page=1_abhilfe-hyper-v-sandbox-nbsp---weg-damit)

 

Lösung des Problems in Windows 10

Drücke auf die Windows Taste und gebe 'Windows-Features aktivieren oder deaktivieren' ein

 

Dann deinstalliert ihr die Features durch das Entfernen der gesetzten Hakens.

  • Hyper-V
  • Windows-Hypervisor-Plattform
  • Windows Sandbox

 

Anschließend Ok und das Betriebssystem neu starten.

 

Lösung des Problems in Windows 11

Im Grunde das gleiche. Nur halt mit runden Ecken. 

An einem anderem System konnte ich feststellen, dass nicht zwingen alle Punkte aufgeführt sein müssen. Denn je nach Hardware Unterstützung werden auch nur die Optionen wieder gegeben, die auch wirklich unterstützt werden.


Montag, 9. Mai 2022

Karosseriebeleuchtung mit PCA9685 (Zusatz)

Eine Sache ist noch offen: Bei meinem vorigen Blogpost konnte man in den Videos sehen, dass die LEDs beim Fade-OUT nie ganz aus waren.

Benötigt werden:

  • Arduino
  • PWM Modul PCA9685
  • LEDs und Widerstände

Ziel

Die LEDs sollen im Fade-OUT komplett ausgehen. Asynchrone Animationsabläufe.


Ursache

Es lag daran, dass der Wert nie den Endwert von 4095 erreicht hat. Auch, wenn das High Level nur sehr kurz anliegt, reicht die Spannungsversorgung aus, um einen LED noch glimmen zu lassen.

Daher liegt die Lösung nahe, dass am Ende eines Schleifendurchgang nochmal ein Signal-Zustand festgelegt wird, mit dem genannten Endwert. Jedoch soll das für die asynchronen Vorgänge auch funktionieren. Hier nochmal das Beispiel, in denen die LEDs nicht ganz aus sind.

 #include <Wire.h>  
 #include <Adafruit_PWMServoDriver.h>  
 Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();  
 void setup() {  
  pwm.begin();  
  pwm.setPWMFreq(1600);  
  Wire.setClock(400000);  
 }  
 void loop() {  
  int stepAdd = 10;  
  int leds = 4;  

  for (uint16_t i=0; i<4096; i += stepAdd) {  
   for (uint8_t pwmnum=0; pwmnum < leds; pwmnum++) {  
    pwm.setPWM(pwmnum, 0, 4096 - i);  
   }  
  }  
  delay(1000);  
  for (uint16_t i=0; i<4096; i += stepAdd) {  
   for (uint8_t pwmnum=0; pwmnum < leds; pwmnum++) {  
    pwm.setPWM(pwmnum, 0, i);  
   }  
  }  
  delay(2000);  
 }  

Einfache Lösung

Mit der bekannten Schrittweite kann nach dem Schleifendurchlauf bedingt die PWM Ports auf den Max Wert eingestellt werden.

 #include <Wire.h>  
 #include <Adafruit_PWMServoDriver.h>  
 Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();  
 void setup() {  
  pwm.begin();  
  pwm.setPWMFreq(1600);  
  Wire.setClock(400000);  
 }  
 void loop() {  
  int stepAdd = 10;  
  int leds = 4;  

  for (uint16_t i=0; i<4096; i += stepAdd) {  
   for (uint8_t pwmnum=0; pwmnum < leds; pwmnum++) {  
    pwm.setPWM(pwmnum, 0, 4096 - i);  
   }  
  }  
  delay(1000);  
  uint16_t iSetValue = 0;  
  for (uint16_t i=0; i < 4096; i += stepAdd) {  
   for (uint8_t pwmnum=0; pwmnum < leds; pwmnum++) {  
    pwm.setPWM(pwmnum, 0, i);  
   }  
   iSetValue = i;  
  }  
  if(iSetValue > 4095 - stepAdd) {  
   for (uint8_t pwmnum=0; pwmnum < leds; pwmnum++) {  
    pwm.setPWM(pwmnum, 0, 4095);  
   }  
  }  
  delay(2000);  
 }  

Lösung für asynchrone Vorgänge

Das gleiche kann auch für den asynchronen Vorgang verwendet werden. Jedoch hat die bisherige Lösung einen Nachteil: Dass sie schnell unübersichtlich wird. 

Die Beispiele habe ich oft nur mit zwei LEDs abgebildet, aber für 16 LEDs wird's schlecht leserlich.

Deshalb gehe ich hier mit der Beispiellösung einen Schritt weiter und lege ein Objekt an, in dem ich für die einzelne LED den Zustand hinterlegen kann.

 #include <Wire.h>  
 #include <Adafruit_PWMServoDriver.h>  
 Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();  
 unsigned long mCurrentMillis = 0;  
 void setup() {  
  pwm.begin();  
  pwm.setPWMFreq(1600);  
  Wire.setClock(400000);  
  // schaltet alle aus  
  for (uint8_t pwmnum=0; pwmnum < 16; pwmnum++) {  
    pwm.setPWM(pwmnum, 0, 4095);  
  }  
 }  
 // status objekt für eine LED  
 typedef struct {  
   unsigned long LastMillis;      // letzte Zeitstand  
   unsigned long DistanceFromNowToLast;// Intervall länge  
   uint16_t Value;           // aktuell verwendeter Wert  
   bool IsFlashing;          // soll nur an und aus  
   bool IsGoingOn;           // Schritt soll heller   
                     // oder an sein  
 } ledStateType;  
 // Konfiguration einzelner LEDs  
 ledStateType mLEDs[] = {  
  { 0, 5, 0, false, true },       // fade animation  
  { 0, 1000, 0, true, true }      // flashing  
 };  
 void PrepareLED(int ledIndex) {  
  int stepAdd = 10;  
  unsigned long rest = mCurrentMillis - mLEDs[ledIndex].LastMillis;  
  if(rest < mLEDs[ledIndex].DistanceFromNowToLast) {  
   return;  
  }  
  if(mLEDs[ledIndex].IsFlashing) {  
   if(mLEDs[ledIndex].IsGoingOn) {  
    mLEDs[ledIndex].Value = 4095;  
    mLEDs[ledIndex].IsGoingOn = false;  
   }  
   else {  
    mLEDs[ledIndex].Value = 0;  
    mLEDs[ledIndex].IsGoingOn = true;  
   }  
  }  
  else {  
   if(mLEDs[ledIndex].IsGoingOn) {  
    if(mLEDs[ledIndex].Value > stepAdd) {  
     mLEDs[ledIndex].Value -= stepAdd;  
    }  
    else {  
     mLEDs[ledIndex].Value = 0;  
     mLEDs[ledIndex].IsGoingOn = false;  
    }  
   }  
   else {  
    if(mLEDs[ledIndex].Value < 4096) {  
     mLEDs[ledIndex].Value += stepAdd;  
    }  
    if(mLEDs[ledIndex].Value > 4095 - stepAdd) {  
     mLEDs[ledIndex].Value = 4095;  
     mLEDs[ledIndex].IsGoingOn = true;  
    }  
   }  
  }  
  mLEDs[ledIndex].LastMillis = mCurrentMillis;  
 }  
 void loop() {  
  mCurrentMillis = millis();  
  for(int i = 0; i < 2; i++) {  
   PrepareLED(i);  
   pwm.setPWM(i, 0, mLEDs[i].Value);  
  }  
 }  



Natürlich ist das nur eine Möglichkeit, wie der Programmablauf aussehen könnte. Der geübte C++ Programmierer würde das sicherlich anders lösen können.



Vorheriger Blog Post:

Elektromotor36GP-555

Der Motor ist heute mit der Post gekommen. Anschließend hatte ich nochmal in die Technischen Daten gesehen. Zugegeben, ich habe den Motorbes...