Introduction

Dieses Repository beinhaltet diverse Zusammenfassungen des Studiengangs 2021 in Informatik an der ZHAW.

Helpers

Hex Helpers

Dec to Hex

decimal = 1

print (f"Hex: {hex(decimal)}")
print (f"Octal: {oct(decimal)}")

Hex to Int

hexadecimal = 0xFF

print(f"Dec: {int(hexadecimal)}")
print(f"Octal: {oct(hexadecimal)}")

SWEN2

exTreme Programming (XP)

Risks in development projects

The role of XP is to give us principles and practives in oder to deal with these risks!

  • Schedule slips
  • Project canceled
  • System does not evolve gracefully (defect rate increses)
  • Defect rate
  • Business misunderstood
  • Business changes
  • False feature rich
  • Staff turnover (knowhow gets lost by employees leaving)

Variables

alt text

  • Time

  • Resources

  • Quality

  • Scope

  • External forces (customers, managers) can set the values for 3 of the variables

  • The development team sets the value for the 4th one

  • It is also possible, to develop cheap software, where automated tests are not of importance

Values

Communication

  • Everyone is part of the team and we communicate face to face daily.
  • We will work together on everything from requirements to code.
  • We will create the best solution to our problem that we can together.

Simplicity

  • We will do what is needed and asked for, but no more.
  • This will maximize the value created for the investment made to date.
  • We will take small simple steps to our goal and mitigate failures as they happen.
  • We will create something we are proud of and maintain it long term for reasonable costs.

Feedback

  • We will take every iteration commitment seriously by delivering working software.
  • We demonstrate our software early and often then listen carefully and make any changes needed.
  • We will talk about the project and adapt our process to it, not the other way around.

Courage

  • We will tell the truth about progress and estimates.
  • We don't document excuses for failure because we plan to succeed.
  • We don't fear anything because no one ever works alone.
  • We will adapt to changes when ever they happen

Respect

  • Everyone gives and feels the respect they deserve as a valued team member.
  • Everyone contributes value even if it's simply enthusiasm.
  • Developers respect the expertise of the customers and vice versa.
  • Management respects our right to accept responsibility and receive authority over our own work.

Cost of Change

Traditional View

alt text

The XP View

alt text

Basic Principles

Fundamental principles

  • Rapid feedback
  • Assume simplicity
  • Incremental change
  • Embracing change
  • Quality work

Other principles (contd)

  • Teach learning
  • Small inital investment
  • Play to win
  • Concrete experiments
  • Open, honest communication
  • Work with people's instincts, not against them
  • Accepted responsibility
  • Local adaption
  • Travel light
  • Honest mesurement

Practices

Planing Game

  • Balance between busines and technical considerations
  • Business people decide about:
    • Scope
    • Priority
    • Composition of releases
    • Dates of releases
  • Technical people decide about:
    • Estimates
    • Consequences
    • Process
    • Detailed Scheduling

Small Releases

  • Every release should be as small as possible, containing the most valuable business requirements
  • The release has to make sense as a whole (no half-working features).
  • Better to release once a month than twice a year.

Metaphor

  • Everybody on the team needs to have a “common understanding” for the system.
  • Everybody on the team needs to have a “shared vocabulary”.
  • This applies to technical and non-technical people.
  • What are the basic elements of the system and what are their relationships?

Simple Design

  • The right design for a software system is one that:
    • Runs all tests.
    • Has no duplicated logic.
    • Has the fewest possible classes and methods.
    • “Put in what need when you need it”.
    • Emergent, growing design; no big design upfront (through refactoring)

Testing

  • Any program feature without an automated test simply doesn’t exist.
  • The tests become part of the system.
  • The tests allow the system to accept change.
  • Development cycle:
    • Listen (requirements)
    • Test (write first)
    • Code (simplest)
    • Design (refactor)

Refactoring

  • When implementing a feature, ask yourself if there is a way to improve the existing source code, so that implementing the feature is easier.
  • Automated tests provide a safety-net for refactoring without fear.

Pair Programming

  • All production code is written by two people looking at one screen, with one keyboard and one mouse.
  • Two roles. The programmer on the keyboard focuses on the current method. The other programmer thinks about the broader context (refactoring, etc.)
  • Pairs change frequently.

Collective Ownership

  • Anybody who sees an opportunity to add value to any portion of the code is required to do so at any time. TTL.
  • Everybody takes responsibility for the whole of the system. Not everyone knows every part equally well, but everyone knows something about every part.

Continous integration

  • Code is integrated and tested at least once a day (sometimes more).
  • Build process must me automated, on a dedicated machine.
  • Automated tests are run and make it possible to identify problems early.

40 hours week

  • Sustainable development. Effort should be spread out evenly.
  • Extended periods of overtime have a negative impact on productivity.
  • Goal: be fresh every morning, be tired and satisfied every evening.
  • Not being in front of a computer does not mean forgetting about the system... taking a step back often leads to “Aha!” moments.

On-site customer

  • A real customer must be physically with the team, available to answer their questions.
  • Real customer = user who will use the system.
  • The real customer does not work on the project 100% of his time, but needs to be “there” to answer questions rapidly.
  • The real customer also help with prioritization.

Codign standards

  • Collective ownership + constant refactoring means that coding practices must be unified in the team.

Primary Practices

  • Sit together
  • whole team
  • informative workspace
  • energized work
  • pair programming
  • stories
  • weekly cycle
  • quarterly cycle
  • slack
  • ten-minute build
  • continous integration
  • test-first programming
  • incremental design

Corollary practices (logische praktiken)

  • Real customer involment
  • incremental deployment
  • team continuity
  • shrinking teams
  • root-cause analysis
  • shared code
  • code adn tests
  • single code base
  • daily deployment
  • negotiated scope contract
  • pay-per-use

CFD

  • Displays stability over time
  • Tracks and accumulates each task with their corresponding stage

alt text

Stages

  • Requested
  • In Progress
  • Done

SWEN2 Prüfungszusammenfassung

Tidy First? (Buch von Kent Beck)

Wichtigste Ideen und Konzepte:

  • Ordnung vor Funktionalität: Die Grundidee ist, dass es einfacher ist, Änderungen in sauberem Code vorzunehmen.
  • Kontinuierliches Aufräumen: Code sollte regelmäßig aufgeräumt werden, um technische Schulden zu vermeiden.
  • Empirisches Softwaredesign: Entscheidungen sollten auf Grundlage von Erfahrungen und Experimenten getroffen werden.

Einführung Softwareentwicklung

SWEBOK:

  • Definition: Software Engineering Body of Knowledge.
  • Inhalt: Umfasst grundlegende Prinzipien, Praktiken und Techniken des Software Engineerings.

Plangetriebene Methoden:

  • Vorteile: Vorhersagbarkeit, klare Struktur und Dokumentation, gut für große, komplexe Projekte.
  • Nachteile: Weniger Flexibilität, schwierig bei sich ändernden Anforderungen.
  • Einsatzgebiete: Große, stabile Projekte mit klaren Anforderungen (z.B. Regierungsprojekte).

Gesetze der Softwareentwicklung

Drei Hauptgesetze:

  • Lehman's Laws: Software altert und muss kontinuierlich weiterentwickelt werden.

Weitere Gesetze:

  • Conway’s Law: Systemdesign spiegelt die Kommunikationsstruktur der Organisation wider.
  • Brooks’s Law: Hinzufügen von mehr Personal zu einem verspäteten Projekt verzögert es weiter.
  • Parkinson’s Law: Arbeit dehnt sich aus, um die verfügbare Zeit zu füllen.
  • Pareto’s Fallacy: 80% der Effekte kommen von 20% der Ursachen.
  • Sturgeon’s Revelation: 90% von allem sind Schrott.
  • The Iceberg Fallacy: Der größte Teil eines Problems ist unsichtbar.
  • The Peter Principle: In einer Hierarchie steigt jeder bis zu seiner Inkompetenz auf.
  • Eagleson’s Law: Jeder Programmierer verdoppelt den Speicherbedarf des Programms.
  • Greenspun’s 10th Rule: Jede komplexe Software enthält ein schlechtes, in sich verborgenes Lisp.

Agile Manifesto

Values:

  • Individuen und Interaktionen über Prozesse und Werkzeuge.
  • Funktionierende Software über umfassende Dokumentation.
  • Zusammenarbeit mit dem Kunden über Vertragsverhandlungen.
  • Reagieren auf Veränderung über das Befolgen eines Plans.

12 Principles:

  1. Frühe und kontinuierliche Auslieferung wertvoller Software.
  2. Begrüßen von sich ändernden Anforderungen.
  3. Lieferung funktionierender Software häufig.
  4. Tägliche Zusammenarbeit zwischen Geschäftsleuten und Entwicklern.
  5. Projekte um motivierte Individuen herum aufgebaut.
  6. Effektive Kommunikation von Angesicht zu Angesicht.
  7. Funktionierende Software als wichtigstes Fortschrittsmaß.
  8. Nachhaltige Entwicklung.
  9. Technische Exzellenz und gutes Design fördern Agilität.
  10. Einfachheit.
  11. Selbstorganisierte Teams.
  12. Regelmäßige Reflexion und Anpassung.

Agile

Begriffe:

  • Risk: Potenzielle Probleme, die den Projekterfolg beeinträchtigen könnten.
  • Cost of Change: Der Aufwand, der nötig ist, um Änderungen am Projekt vorzunehmen.
  • Iron Triangle: Die drei Variablen Scope, Time, und Cost beeinflussen sich gegenseitig.

eXtreme Programming (XP)

Core Values:

  • Kommunikation, Einfachheit, Feedback, Mut, Respekt.

Principles:

  • Menschliche Werte, Wirtschaftlichkeit, gegenseitiger Nutzen, Selbstähnlichkeit, kontinuierliche Verbesserung.

Practices:

  • Planungsspiel, kleine Releases, Metapher, einfaches Design, Tests, Refactoring, Pair Programming, kollektive Code-Eigentümerschaft, kontinuierliche Integration, 40-Stunden-Woche, Vor-Ort-Kunde, Coding-Standards, TDD, Slack, Spike, inkrementelles Design, selbstorganisiertes Team.

Software Craft

Notwendigkeit und Prinzipien:

  • Notwendigkeit: Reaktion auf mangelhafte Softwarequalität und -praktiken.
  • Formate: Coding Dojo, Code Katas zur Übung und Verbesserung der Programmierfähigkeiten.
  • Community-Prinzipien: Kontinuierliches Lernen und Teilen von Wissen.

Pyramid of Agile Competencies

Drei Ebenen:

  • Technische Praktiken: Grundlagen wie Testen und Refactoring.
  • Team Praktiken: Zusammenarbeit und Kommunikation.
  • Business Praktiken: Kundenorientierung und Wertschöpfung.

User Stories

Ziele und Anwendung:

  • Ziele: Verständnis der Nutzerbedürfnisse.
  • Anwendung: Agile Planung und Priorisierung.
  • Format: "Als [Rolle] möchte ich [Funktion], um [Nutzen]."
  • Zusammenhang: Epics (große Stories), Themes (thematische Gruppierung), Stories (kleinere, umsetzbare Einheiten).
  • Merkmale: Unabhängig, verhandelbar, wertvoll, schätzbar, klein, testbar.

Estimation and Planning

Begriffe:

  • User Stories, User Roles, Epics, Themes, Story Points, Velocity, Planning Poker, Conditions of Satisfaction, Levels of Planning, Product Backlog, Priorisierung.
  • Schätzungstechniken: Relativ, Planning Poker.
  • Planung für Wert: Kosten, finanzieller Wert, Risiko, neues Wissen, Kano-Modell der Kundenzufriedenheit.

Build Automation, CI, CD, DevOps

Arten der Automatisierung:

  • On-demand, scheduled, triggered.

Pipeline:

  • Schritte von Code-Commit bis Deployment.

Scrum

Charakteristika und Begriffe:

  • Scrum-Rollen: Scrum Master, Product Owner, Team.
  • Scrum-Events: Sprint, Daily Scrum, Review, Retrospektive.
  • Scrum-Artefakte: Product Backlog, Sprint Backlog, Increment.
  • Scrum-Werte: Mut, Fokus, Offenheit, Respekt, Commitment.

Begriffe:

  • Sprint, Sprintziel, Retrospektive, Task Board, Burndown Chart, Definition of Done, Definition of Ready, Daily Scrum, Increment.

Architektur Patterns (Gruppenpuzzle)

Patterns:

  • CQRS: Trennung von Lese- und Schreiboperationen.
  • Event Sourcing: Speicherung aller Zustandsänderungen als Ereignisse.
  • Strangler Pattern: Schrittweise Ersetzung eines alten Systems durch ein neues.
  • Online-Migration: Live-Migration von Daten.
  • Circuit Breaker: Schutz vor Ausfällen durch Überlastung.
  • Bulkhead: Isolation von Systemkomponenten.
  • Retry: Wiederholungsmechanismen bei Fehlern.
  • Serverless: Betrieb von Anwendungen ohne Verwaltung von Servern.
  • Microservices: Modularisierung von Anwendungen in kleine, unabhängige Dienste.
  • Self-contained Systems: Autonome Systeme mit eigener Datenhaltung.
  • Monolith (Modulith): Großer, zusammenhängender Codeblock, der in Module aufgeteilt werden kann.

Software Architecture

Grundbegriffe:

  • Software-Architektur: Struktur und Organisation eines Softwaresystems.
  • Software-Architekt: Verantwortlich für das Design und die Integrität der Architektur.
  • Enterprise Architecture vs. Application Architecture: Unternehmensweite vs. anwendungsspezifische Architektur.
  • Schwierigkeiten: Komplexität, Konformität, Änderbarkeit, Unsichtbarkeit.

Architectural Drivers:

  • Faktoren, die die Architektur beeinflussen (Geschäftsanforderungen, technische Anforderungen).

Standards, Stile, Patterns:

  • Standardisierte Lösungen für wiederkehrende Probleme.
  • Unterschiede zwischen Layer und Tier.

Architekturstile:

  • Vor- und Nachteile von Stilen wie Monolith, Microservices.

Patterns:

  • Transaction Script: Verarbeiten von Transaktionen durch einfache Skripte.
  • Domain Model: Modellierung der Geschäftslogik.
  • Table Module: Datenbanktabellen als zentrale Logikstrukturen.

C4-Modell:

  • Kontextsensitive Architekturmodellierung.

Architecture Canvas:

  • Werkzeug zur Visualisierung und Planung von Architekturen.

Cynefin Framework / Codefin

Begriffe:

  • Cynefin (Komplexitätsframework), Exaptation (Nutzung bestehender Strukturen für neue Zwecke), 4 + 1 Domains (Einfach, Kompliziert, Komplex, Chaotisch, Unordnung), Causality, Correlation, Constraint.

Anwendung:

  • Nutzung des Cynefin-Frameworks zur Entscheidungsfindung und Problemlösung in der Softwareentwicklung.

Kanban / Lean Software Development

Zusammenhang und Herkunft:

  • Agile, Lean, Scrum, XP und Kanban stammen aus der Lean-Philosophie.

Kanban-Prinzipien:

  • Visualisieren des Workflows, Limitierung von WIP (Work in Progress), Management des Flusses.

Kanban Board:

  • Werkzeug zur Visualisierung des Workflows.

Cumulative Flow Diagram (CFD):

  • Darstellung des Arbeitsflusses zur Identifikation von Engpässen.

Einführung in die Softwareentwicklung

Die Softwareentwicklung ist ein komplexer Prozess, der verschiedene Phasen durchläuft, darunter Planung, Design, Implementierung, Testen, Bereitstellung und Wartung. Ein systematischer Ansatz ist erforderlich, um hochwertige Softwareprodukte zu liefern, die den Anforderungen der Benutzer entsprechen und innerhalb der geplanten Zeit und des Budgets fertiggestellt werden.

SWEBOK: Software Engineering Body of Knowledge

SWEBOK steht für "Software Engineering Body of Knowledge" und ist eine Sammlung von grundlegenden Prinzipien und Praktiken, die für das Software Engineering als Disziplin anerkannt sind. SWEBOK deckt verschiedene Wissensgebiete ab, darunter Anforderungen, Design, Konstruktion, Testen, Wartung, Projektmanagement, Qualitätssicherung und mehr. Es dient als Leitfaden für Software-Ingenieure und hilft dabei, Best Practices zu definieren und zu standardisieren.

Plangetriebene Methoden in der Softwareentwicklung

Plangetriebene Methoden, auch als traditionelle oder sequentielle Methoden bekannt, beinhalten eine umfassende Planung und Dokumentation vor Beginn der eigentlichen Entwicklungsarbeit. Ein typisches Beispiel für eine plangetriebene Methode ist das Wasserfallmodell, bei dem jede Phase des Entwicklungsprozesses in einer festen Reihenfolge durchgeführt wird.

Vorteile der plangetriebenen Methoden

  1. Klare Struktur und Reihenfolge:

    • Jede Phase hat klar definierte Ziele und Outputs.
    • Einfacher zu verfolgen und zu dokumentieren.
  2. Vollständige Dokumentation:

    • Umfassende Dokumentation ist in jeder Phase erforderlich.
    • Hilfreich für zukünftige Wartung und Überprüfung.
  3. Vorhersagbarkeit:

    • Einfachere Planung von Ressourcen und Zeitplänen.
    • Risiken können frühzeitig identifiziert und gemanagt werden.
  4. Einheitliche Anforderungen:

    • Anforderungen werden zu Beginn des Projekts klar definiert und dokumentiert.
    • Geringeres Risiko von Anforderungsänderungen im späteren Verlauf.

Nachteile der plangetriebenen Methoden

  1. Unflexibilität:

    • Änderungen sind schwer und teuer umzusetzen, sobald das Projekt begonnen hat.
    • Schwierigkeiten beim Umgang mit sich ändernden Anforderungen.
  2. Lange Vorlaufzeit:

    • Lange Phasen der Planung und Dokumentation können den Projektstart verzögern.
    • Markt- oder Geschäftsanforderungen könnten sich ändern, bevor die Software fertiggestellt ist.
  3. Späte Lieferung:

    • Funktionalitäten werden erst spät im Entwicklungsprozess geliefert und getestet.
    • Kundenfeedback kommt oft zu spät, um in das aktuelle Projekt einfließen zu können.
  4. Hoher Verwaltungsaufwand:

    • Erheblicher Zeit- und Ressourcenaufwand für die Erstellung und Pflege der Dokumentation.
    • Potenzial für Bürokratie und ineffiziente Prozesse.

Geeignete Projekte für plangetriebene Methoden

Plangetriebene Methoden eignen sich besonders für:

  • Große und komplexe Projekte:

    • Wo eine detaillierte Planung und umfassende Dokumentation unerlässlich sind.
    • Beispiele: Bau von Betriebssystemen, sicherheitskritische Systeme (z. B. in der Luftfahrt).
  • Projekte mit stabilen Anforderungen:

    • Wo die Anforderungen zu Beginn des Projekts gut verstanden und unwahrscheinlich sind, sich zu ändern.
    • Beispiele: Langfristige, behördlich regulierte Projekte, wie z. B. medizinische Software.

Projekte, für die plangetriebene Methoden weniger geeignet sind

Plangetriebene Methoden sind weniger geeignet für:

  • Dynamische und sich schnell ändernde Projekte:

    • Wo die Anforderungen und Prioritäten häufig wechseln.
    • Beispiele: Startups, Web- und Mobile-Anwendungen, die auf Nutzerfeedback angewiesen sind.
  • Kleinere, agile Projekte:

    • Wo schnelle Iterationen und regelmäßige Releases erforderlich sind.
    • Beispiele: Agile Entwicklungsteams, die Scrum oder Kanban verwenden.

Fazit

Plangetriebene Methoden bieten klare Vorteile in Bezug auf Struktur, Dokumentation und Vorhersagbarkeit, sind jedoch weniger flexibel und anpassungsfähig an sich ändernde Anforderungen. Die Wahl der Methode sollte basierend auf den spezifischen Projektanforderungen und -bedingungen erfolgen. In dynamischen und sich schnell ändernden Umgebungen sind agile Methoden oft besser geeignet, während plangetriebene Ansätze in stabileren und gut definierten Projekten Vorteile bieten können.

Einführung in die Softwareentwicklung

Die Softwareentwicklung ist ein komplexer Prozess, der verschiedene Phasen durchläuft, darunter Planung, Design, Implementierung, Testen, Bereitstellung und Wartung. Ein systematischer Ansatz ist erforderlich, um hochwertige Softwareprodukte zu liefern, die den Anforderungen der Benutzer entsprechen und innerhalb der geplanten Zeit und des Budgets fertiggestellt werden.

SWEBOK: Software Engineering Body of Knowledge

SWEBOK steht für "Software Engineering Body of Knowledge" und ist eine Sammlung von grundlegenden Prinzipien und Praktiken, die für das Software Engineering als Disziplin anerkannt sind. SWEBOK deckt verschiedene Wissensgebiete ab, darunter Anforderungen, Design, Konstruktion, Testen, Wartung, Projektmanagement, Qualitätssicherung und mehr. Es dient als Leitfaden für Software-Ingenieure und hilft dabei, Best Practices zu definieren und zu standardisieren.

Plangetriebene Methoden in der Softwareentwicklung

Plangetriebene Methoden, auch als traditionelle oder sequentielle Methoden bekannt, beinhalten eine umfassende Planung und Dokumentation vor Beginn der eigentlichen Entwicklungsarbeit. Ein typisches Beispiel für eine plangetriebene Methode ist das Wasserfallmodell, bei dem jede Phase des Entwicklungsprozesses in einer festen Reihenfolge durchgeführt wird.

Vorteile der plangetriebenen Methoden

  1. Klare Struktur und Reihenfolge:

    • Jede Phase hat klar definierte Ziele und Outputs.
    • Einfacher zu verfolgen und zu dokumentieren.
  2. Vollständige Dokumentation:

    • Umfassende Dokumentation ist in jeder Phase erforderlich.
    • Hilfreich für zukünftige Wartung und Überprüfung.
  3. Vorhersagbarkeit:

    • Einfachere Planung von Ressourcen und Zeitplänen.
    • Risiken können frühzeitig identifiziert und gemanagt werden.
  4. Einheitliche Anforderungen:

    • Anforderungen werden zu Beginn des Projekts klar definiert und dokumentiert.
    • Geringeres Risiko von Anforderungsänderungen im späteren Verlauf.

Nachteile der plangetriebenen Methoden

  1. Unflexibilität:

    • Änderungen sind schwer und teuer umzusetzen, sobald das Projekt begonnen hat.
    • Schwierigkeiten beim Umgang mit sich ändernden Anforderungen.
  2. Lange Vorlaufzeit:

    • Lange Phasen der Planung und Dokumentation können den Projektstart verzögern.
    • Markt- oder Geschäftsanforderungen könnten sich ändern, bevor die Software fertiggestellt ist.
  3. Späte Lieferung:

    • Funktionalitäten werden erst spät im Entwicklungsprozess geliefert und getestet.
    • Kundenfeedback kommt oft zu spät, um in das aktuelle Projekt einfließen zu können.
  4. Hoher Verwaltungsaufwand:

    • Erheblicher Zeit- und Ressourcenaufwand für die Erstellung und Pflege der Dokumentation.
    • Potenzial für Bürokratie und ineffiziente Prozesse.

Geeignete Projekte für plangetriebene Methoden

Plangetriebene Methoden eignen sich besonders für:

  • Große und komplexe Projekte:

    • Wo eine detaillierte Planung und umfassende Dokumentation unerlässlich sind.
    • Beispiele: Bau von Betriebssystemen, sicherheitskritische Systeme (z. B. in der Luftfahrt).
  • Projekte mit stabilen Anforderungen:

    • Wo die Anforderungen zu Beginn des Projekts gut verstanden und unwahrscheinlich sind, sich zu ändern.
    • Beispiele: Langfristige, behördlich regulierte Projekte, wie z. B. medizinische Software.

Projekte, für die plangetriebene Methoden weniger geeignet sind

Plangetriebene Methoden sind weniger geeignet für:

  • Dynamische und sich schnell ändernde Projekte:

    • Wo die Anforderungen und Prioritäten häufig wechseln.
    • Beispiele: Startups, Web- und Mobile-Anwendungen, die auf Nutzerfeedback angewiesen sind.
  • Kleinere, agile Projekte:

    • Wo schnelle Iterationen und regelmäßige Releases erforderlich sind.
    • Beispiele: Agile Entwicklungsteams, die Scrum oder Kanban verwenden.

Fazit

Plangetriebene Methoden bieten klare Vorteile in Bezug auf Struktur, Dokumentation und Vorhersagbarkeit, sind jedoch weniger flexibel und anpassungsfähig an sich ändernde Anforderungen. Die Wahl der Methode sollte basierend auf den spezifischen Projektanforderungen und -bedingungen erfolgen. In dynamischen und sich schnell ändernden Umgebungen sind agile Methoden oft besser geeignet, während plangetriebene Ansätze in stabileren und gut definierten Projekten Vorteile bieten können.

Values des Agile Manifesto

Das Agile Manifesto wurde im Jahr 2001 von einer Gruppe von Softwareentwicklern veröffentlicht, um eine neue Herangehensweise an die Softwareentwicklung zu fördern, die flexibler und anpassungsfähiger ist. Es basiert auf vier zentralen Werten:

  1. Individuen und Interaktionen über Prozesse und Werkzeuge

    • Bedeutung: Der Schwerpunkt liegt auf der Zusammenarbeit und Kommunikation der Menschen, die das Projekt durchführen. Werkzeuge und Prozesse sind wichtig, aber sie sollten nicht die menschlichen Interaktionen ersetzen.
  2. Funktionierende Software über umfassende Dokumentation

    • Bedeutung: Das Hauptziel ist es, funktionierende Software zu liefern, die den Bedürfnissen der Kunden entspricht. Dokumentation ist wichtig, aber sie sollte nicht die Entwicklung funktionierender Software behindern.
  3. Zusammenarbeit mit dem Kunden über Vertragsverhandlungen

    • Bedeutung: Eine enge Zusammenarbeit mit dem Kunden wird gegenüber starren Vertragsverhandlungen bevorzugt. Es geht darum, gemeinsam an Lösungen zu arbeiten, anstatt nur vertragliche Verpflichtungen zu erfüllen.
  4. Reagieren auf Veränderung über das Befolgen eines Plans

    • Bedeutung: Agile Methoden betonen die Fähigkeit, auf Veränderungen flexibel zu reagieren, anstatt strikt einem vorgefassten Plan zu folgen. Anpassungsfähigkeit ist entscheidend für den Erfolg in einer sich ständig ändernden Umgebung.

12 Principles des Agile Manifesto

Hinter den Werten des Agile Manifesto stehen 12 Prinzipien, die genauer beschreiben, wie diese Werte in der Praxis umgesetzt werden können:

  1. Unsere höchste Priorität ist es, den Kunden durch frühzeitige und kontinuierliche Auslieferung wertvoller Software zufrieden zu stellen.

    • Bedeutung: Der Fokus liegt darauf, dem Kunden schnell funktionierende Software zu liefern und kontinuierlich zu verbessern.
  2. Willkommen ändern Anforderungen, auch spät in der Entwicklung. Agile Prozesse nutzen Veränderungen zum Wettbewerbsvorteil des Kunden.

    • Bedeutung: Änderungen werden begrüßt und als Möglichkeit gesehen, das Produkt zu verbessern und den Wettbewerbsvorteil zu erhöhen.
  3. Liefern Sie häufig funktionierende Software, im Abstand von wenigen Wochen bis zu wenigen Monaten, mit Präferenz für die kürzere Zeitspanne.

    • Bedeutung: Regelmäßige, kleine Releases helfen dabei, schnell Feedback zu erhalten und das Produkt iterativ zu verbessern.
  4. Fachleute und Entwickler müssen während des Projekts täglich zusammenarbeiten.

    • Bedeutung: Eine enge Zusammenarbeit zwischen Fachexperten und Entwicklern ist entscheidend für den Projekterfolg.
  5. Bauen Sie Projekte um motivierte Individuen. Geben Sie ihnen das Umfeld und die Unterstützung, die sie brauchen, und vertrauen Sie ihnen, die Arbeit zu erledigen.

    • Bedeutung: Motivierte und unterstützte Teammitglieder sind der Schlüssel zu erfolgreichen Projekten.
  6. Der effizienteste und effektivste Weg, Informationen an und innerhalb eines Entwicklungsteams zu übermitteln, ist das Gespräch von Angesicht zu Angesicht.

    • Bedeutung: Direkte Kommunikation ist der beste Weg, um Missverständnisse zu vermeiden und klare Informationen zu übermitteln.
  7. Funktionierende Software ist das wichtigste Fortschrittsmaß.

    • Bedeutung: Der Erfolg eines Projekts wird daran gemessen, ob funktionierende Software geliefert wird, nicht an der Erfüllung von Plänen oder der Erstellung von Dokumentation.
  8. Agile Prozesse fördern nachhaltige Entwicklung. Die Auftraggeber, Entwickler und Benutzer sollten in einem gleichmäßigen Tempo unbegrenzt zusammenarbeiten können.

    • Bedeutung: Ein nachhaltiges Arbeitstempo sorgt dafür, dass Teams langfristig produktiv bleiben.
  9. Ständiges Augenmerk auf technische Exzellenz und gutes Design verbessert die Agilität.

    • Bedeutung: Hohe technische Qualität und gutes Design sind entscheidend für die Fähigkeit, sich schnell an Veränderungen anzupassen.
  10. Einfachheit – die Kunst, die Menge nicht getaner Arbeit zu maximieren – ist essenziell.

    • Bedeutung: Der Fokus liegt auf dem Wesentlichen, um unnötige Arbeit zu vermeiden.
  11. Die besten Architekturen, Anforderungen und Entwürfe entstehen durch selbstorganisierte Teams.

    • Bedeutung: Teams, die selbstorganisiert arbeiten, sind oft kreativer und produktiver.
  12. In regelmäßigen Abständen reflektiert das Team, wie es effektiver werden kann, und passt sein Verhalten entsprechend an.

    • Bedeutung: Kontinuierliche Verbesserung durch regelmäßige Selbstreflexion und Anpassung der Arbeitsweisen ist entscheidend.

Bedeutung des Agile Manifesto für die Softwareentwicklung

Das Agile Manifesto hat die Softwareentwicklung grundlegend verändert, indem es den Fokus auf Flexibilität, Zusammenarbeit und Kundenzufriedenheit gelegt hat. Agile Methoden wie Scrum, Kanban und Extreme Programming (XP) sind direkt aus den Prinzipien des Agile Manifesto entstanden und haben sich weit verbreitet.

Wesentliche Vorteile der agilen Ansätze sind:

  • Schnellere Lieferung von funktionierender Software: Durch häufige Releases können Teams schneller auf Kundenfeedback reagieren und die Software kontinuierlich verbessern.
  • Bessere Anpassungsfähigkeit: Agile Teams können schnell auf Veränderungen reagieren und die Richtung des Projekts entsprechend anpassen.
  • Verbesserte Zusammenarbeit und Kommunikation: Engere Zusammenarbeit zwischen Entwicklern und Kunden führt zu besseren Ergebnissen und zufriedeneren Kunden.
  • Erhöhte Motivation und Produktivität: Selbstorganisierte Teams, die in einem unterstützenden Umfeld arbeiten, sind motivierter und produktiver.

Insgesamt hat das Agile Manifesto dazu beigetragen, die Softwareentwicklung effizienter und effektiver zu gestalten, indem es die Bedürfnisse der Kunden in den Mittelpunkt stellt und eine Kultur der kontinuierlichen Verbesserung fördert.

Begriffe in der agilen Softwareentwicklung

Risk (Risiko)

In der Softwareentwicklung bezeichnet Risiko die Möglichkeit, dass ein Projektziel nicht erreicht wird oder unerwünschte Ereignisse eintreten, die den Projekterfolg gefährden könnten. Risiken können technischer, personeller, organisatorischer oder externer Natur sein und umfassen beispielsweise:

  • Technische Risiken: Fehler in der Technologie, unzureichende Leistung, Integrationsprobleme.
  • Personelle Risiken: Mangel an Fachkräften, Ausfall wichtiger Teammitglieder.
  • Organisatorische Risiken: Änderungen in der Unternehmensstruktur, mangelnde Unterstützung durch das Management.
  • Externe Risiken: Gesetzesänderungen, wirtschaftliche Schwankungen, Konkurrenzprodukte.

Agile Methoden versuchen, Risiken durch iterative und inkrementelle Ansätze zu minimieren, indem sie häufige Feedbackschleifen und regelmäßige Anpassungen fördern.

Cost of Change (Kosten der Veränderung)

Die Kosten der Veränderung beziehen sich auf die Ressourcen (Zeit, Geld, Aufwand), die erforderlich sind, um Änderungen am Projekt oder Produkt vorzunehmen. In traditionellen, plangetriebenen Entwicklungsansätzen steigen die Kosten der Veränderung im Laufe des Projekts exponentiell an. Dies liegt daran, dass späte Änderungen oft tiefgreifende Auswirkungen haben und umfangreiche Anpassungen in Design, Code und Tests erforderlich machen.

In agilen Methoden werden die Kosten der Veränderung durch regelmäßige, iterative Entwicklungszyklen und kontinuierliches Feedback reduziert. Änderungen werden frühzeitig und häufig vorgenommen, was bedeutet, dass Anpassungen weniger kostspielig und einfacher umzusetzen sind.

Variablen der Softwareentwicklung (Iron Triangle) und deren Kräfte

Das Iron Triangle (Eisernes Dreieck) der Projektmanagement beschreibt die drei Hauptvariablen, die jedes Projekt beeinflussen: Umfang (Scope), Zeit (Time) und Kosten (Cost). Diese drei Faktoren sind eng miteinander verbunden und beeinflussen sich gegenseitig.

  1. Umfang (Scope):

    • Dies umfasst die Gesamtheit der Arbeiten, die erforderlich sind, um ein Projekt abzuschließen, einschließlich der Anforderungen und Spezifikationen. Änderungen im Umfang können die Zeit und Kosten beeinflussen.
  2. Zeit (Time):

    • Der Zeitrahmen, innerhalb dessen das Projekt abgeschlossen werden muss. Eine Verkürzung der Zeitrahmen kann die Kosten erhöhen und/oder den Umfang einschränken.
  3. Kosten (Cost):

    • Das Budget, das für das Projekt zur Verfügung steht. Erhöhte Kosten können den Umfang erweitern und/oder die Zeit verkürzen.

Kräfte im Iron Triangle:

  • Qualität: Eine vierte Variable, die oft im Zentrum des Dreiecks dargestellt wird. Qualität kann durch Kompromisse zwischen Umfang, Zeit und Kosten beeinflusst werden. Agile Methoden zielen darauf ab, Qualität durch regelmäßige Tests, Refactoring und kontinuierliche Integration zu erhalten.

Beziehungen und Kompromisse:

  • Wenn eine Variable angepasst wird, müssen die anderen entsprechend angepasst werden, um das Gleichgewicht zu halten. Beispielsweise:
    • Mehr Umfang: Erfordert entweder mehr Zeit, mehr Kosten oder beides.
    • Weniger Zeit: Erfordert entweder eine Reduzierung des Umfangs, eine Erhöhung der Kosten oder eine Kombination aus beidem.
    • Geringere Kosten: Erfordert entweder eine Reduzierung des Umfangs, eine Verlängerung der Zeit oder eine Kombination aus beidem.

Agile Ansätze und das Iron Triangle:

  • Agile Methoden bieten Flexibilität bei der Anpassung dieser Variablen durch iterative Entwicklungszyklen und kontinuierliche Priorisierung der Anforderungen. Dies ermöglicht es Teams, schneller auf Änderungen zu reagieren und das Gleichgewicht zwischen Umfang, Zeit und Kosten dynamisch anzupassen, um die bestmögliche Qualität zu liefern.

Zusammenfassung

Die Begriffe Risiko, Kosten der Veränderung und die Variablen des Iron Triangle sind zentrale Konzepte in der Softwareentwicklung, insbesondere im agilen Kontext. Agile Methoden helfen dabei, Risiken zu minimieren und die Kosten der Veränderung zu senken, indem sie auf iterative Entwicklungszyklen, kontinuierliches Feedback und flexible Anpassung der Projektvariablen setzen. Dies fördert eine höhere Qualität und bessere Anpassungsfähigkeit an sich ändernde Anforderungen und Bedingungen.

eXtreme Programming (XP)

eXtreme Programming (XP) ist eine agile Softwareentwicklungsmethode, die darauf abzielt, die Flexibilität und Qualität der Software durch intensive Zusammenarbeit, ständige Kommunikation und iterative Entwicklungsprozesse zu maximieren. XP wurde in den späten 1990er Jahren von Kent Beck entwickelt und konzentriert sich auf Kundenzufriedenheit und die Lieferung von wertvoller Software in kurzen Entwicklungszyklen.

Core Values

  1. Communication (Kommunikation):

    • Offene und häufige Kommunikation zwischen allen Beteiligten ist entscheidend, um Missverständnisse zu vermeiden und schnell auf Änderungen reagieren zu können.
  2. Simplicity (Einfachheit):

    • Das Design und der Code sollten so einfach wie möglich gehalten werden. Überflüssige Komplexität sollte vermieden werden, um die Wartbarkeit und Erweiterbarkeit zu erhöhen.
  3. Feedback (Rückmeldung):

    • Regelmäßiges Feedback von Kunden und Teammitgliedern hilft, die Entwicklung kontinuierlich zu verbessern und sicherzustellen, dass die Software den Anforderungen entspricht.
  4. Courage (Mut):

    • Teammitglieder sollten den Mut haben, Änderungen vorzunehmen, Fehler zuzugeben und Risiken einzugehen, um die beste Lösung zu finden.
  5. Respect (Respekt):

    • Alle Teammitglieder sollten einander respektieren und die Meinungen und Beiträge jedes Einzelnen schätzen.

Principles

  • Rapid Feedback: Schnelles und häufiges Feedback von Kunden und Tests.
  • Assume Simplicity: Annahme der einfachsten Lösung, die funktionieren könnte.
  • Incremental Change: Schrittweise und kleine Änderungen, die leicht zu testen und zu überprüfen sind.
  • Embrace Change: Akzeptanz und Begrüßung von Änderungen als Chance zur Verbesserung.
  • Quality Work: Hohe Qualität der Arbeit wird durch ständige Verbesserung und Tests gewährleistet.

Practices

The Planning Game

  • Bedeutung: Kunden und Entwickler planen gemeinsam die nächsten Iterationen, indem sie die Anforderungen priorisieren und die Umsetzung schätzen.

Small Releases

  • Bedeutung: Häufige, kleine Releases liefern kontinuierlich funktionierende Software an den Kunden, um Feedback zu ermöglichen und Risiken zu minimieren.

Metaphor

  • Bedeutung: Eine gemeinsame Metapher, die das System beschreibt und allen Beteiligten hilft, das Gesamtsystem und seine Architektur zu verstehen.

Simple Design

  • Bedeutung: Das Design sollte einfach und direkt sein, um nur die aktuell benötigte Funktionalität zu unterstützen.

Unit-Testing

  • Bedeutung: Automatisierte Unit-Tests werden geschrieben, um sicherzustellen, dass jede Komponente des Systems korrekt funktioniert.

Refactoring

  • Bedeutung: Der Code wird regelmäßig umstrukturiert, um die interne Struktur zu verbessern, ohne das externe Verhalten zu ändern.

Pair-Programming

  • Bedeutung: Zwei Entwickler arbeiten gemeinsam an einem Computer, um die Qualität des Codes zu verbessern und Wissen zu teilen.

Collective Code Ownership

  • Bedeutung: Jeder im Team kann Änderungen an jedem Teil des Codes vornehmen, um Engpässe zu vermeiden und die Verantwortlichkeit zu teilen.

Continuous Integration

  • Bedeutung: Der Code wird häufig integriert und getestet, um Fehler frühzeitig zu erkennen und zu beheben.

40 Hours Week

  • Bedeutung: Eine normale Arbeitswoche (40 Stunden) wird angestrebt, um Überarbeitung zu vermeiden und die Produktivität langfristig zu erhalten.

On-site Customer

  • Bedeutung: Ein Kunde ist ständig vor Ort, um Fragen zu beantworten und Anforderungen zu klären.

Coding Standards

  • Bedeutung: Einhaltung einheitlicher Kodierungsstandards, um die Lesbarkeit und Wartbarkeit des Codes zu gewährleisten.

Test-Driven Development (TDD)

  • Bedeutung: Tests werden vor dem eigentlichen Code geschrieben, um sicherzustellen, dass der Code die Anforderungen erfüllt.

Slack

  • Bedeutung: Pufferzeit in den Zeitplänen, um unerwartete Probleme bewältigen zu können, ohne den gesamten Zeitplan zu gefährden.

Spike

  • Bedeutung: Kurzfristige, explorative Entwicklung, um technische Probleme zu lösen oder die Machbarkeit einer Idee zu testen.

Incremental Design

  • Bedeutung: Design und Architektur werden schrittweise entwickelt und verbessert, um flexibel auf Änderungen reagieren zu können.

Self-Organized Team

  • Bedeutung: Teams organisieren sich selbst, treffen Entscheidungen gemeinsam und sind für ihre Arbeit verantwortlich.

Cost of Change

In XP wird angenommen, dass die Kosten für Änderungen durch kontinuierliche Tests, Refactoring und enge Zusammenarbeit mit dem Kunden minimiert werden können. Dies steht im Gegensatz zu traditionellen Ansätzen, bei denen die Kosten für Änderungen im Verlauf des Projekts exponentiell steigen.

Business Value

Der Fokus von XP liegt auf der Lieferung von Software, die für das Geschäft des Kunden wertvoll ist. Durch kontinuierliche Zusammenarbeit und regelmäßiges Feedback wird sichergestellt, dass die entwickelten Features tatsächlich den Bedürfnissen und Prioritäten des Kunden entsprechen.

Zusammenfassung

eXtreme Programming ist eine agile Methode, die durch intensive Zusammenarbeit, iterative Prozesse und ständige Verbesserung eine hohe Softwarequalität und Kundenzufriedenheit anstrebt. Die praktischen Ansätze und Prinzipien von XP helfen, flexibel auf Änderungen zu reagieren, Risiken zu minimieren und wertvolle Software effizient zu liefern.

Software Craft

Gründe für das Software Craft Manifesto

Das Software Craft Manifesto wurde notwendig, weil viele in der Softwareentwicklung tätige Personen die Notwendigkeit sahen, über die Prinzipien und Praktiken der agilen Methodik hinauszugehen, um eine Kultur der handwerklichen Qualität zu fördern. Hauptgründe waren:

  1. Mangelnde Qualität: Trotz agiler Methoden wurde oft minderwertige Software produziert, die schwer wartbar war.
  2. Kontinuierliche Verbesserung: Es bestand ein Bedürfnis nach einem ständigen Streben nach Exzellenz und Verbesserung der Fähigkeiten.
  3. Gemeinschaft und Lernen: Der Austausch von Wissen und Erfahrung zwischen Entwicklern sollte gefördert werden.
  4. Berufsstolz: Es wurde betont, dass Softwareentwicklung ein Handwerk ist, auf das man stolz sein sollte, ähnlich wie traditionelle Handwerksberufe.

Das Manifesto for Software Craftsmanship ergänzt das Agile Manifesto mit einem Fokus auf Exzellenz in der Softwareentwicklung:

  1. Nicht nur funktionierende Software, sondern auch gut gestaltete Software
  2. Nicht nur auf Änderungen reagieren, sondern auch stetig Mehrwert liefern
  3. Nicht nur Individuen und Interaktionen, sondern auch eine Gemeinschaft professioneller Entwickler
  4. Nicht nur Kundenkollaboration, sondern auch produktive Partnerschaften

Formate der Software Craft Community

  1. Coding Dojos: Regelmäßige Treffen, bei denen Entwickler zusammenkommen, um Programmieraufgaben in einer kollaborativen Umgebung zu lösen und voneinander zu lernen.
  2. Code Katas: Übungen, bei denen Entwickler wiederholt bestimmte Programmierprobleme lösen, um ihre Fähigkeiten zu verfeinern.
  3. Pair Programming: Zwei Entwickler arbeiten gemeinsam an einem Computer, um Wissen zu teilen und die Codequalität zu verbessern.
  4. Code Retreats: Ganztägige Veranstaltungen, bei denen Entwickler durch intensive Praxis ihre Fähigkeiten verbessern und neue Techniken erlernen.
  5. Tech Talks und Meetups: Präsentationen und Diskussionen zu spezifischen Themen der Softwareentwicklung.
  6. Online Communities und Foren: Plattformen wie Stack Overflow, GitHub und andere, wo Entwickler Fragen stellen, Antworten geben und ihr Wissen teilen können.

Prinzipien der Software Craft Community

  1. Meisterschaft: Streben nach Exzellenz in der Softwareentwicklung.
  2. Kollaboration: Zusammenarbeit und gemeinsames Lernen sind zentral.
  3. Kontinuierliches Lernen: Ständige Weiterbildung und Verbesserung der Fähigkeiten.
  4. Praktische Übung: Regelmäßige, praxisnahe Übung zur Vertiefung und Verfeinerung der Fähigkeiten.
  5. Verantwortung: Verantwortung für die Qualität des Codes und dessen Auswirkungen auf das Team und die Benutzer übernehmen.

Üben als Softwareentwickler:in

Softwareentwickler können ihre Fähigkeiten durch verschiedene Methoden verbessern:

  1. Coding Dojo: Ein Coding Dojo ist eine kollaborative Programmierübung, bei der Entwickler zusammenkommen, um an einer gemeinsamen Aufgabe zu arbeiten. Es fördert das Lernen durch Zusammenarbeit und ermöglicht es den Teilnehmern, voneinander zu lernen. Typischerweise gibt es verschiedene Formate:

    • Randori: Eine Live-Coding-Session, bei der ein Paar von Entwicklern (Driver und Navigator) den Code schreibt, während der Rest des Teams zuschaut und Feedback gibt.
    • Kata: Einzelne oder Paare arbeiten an einer Aufgabe und präsentieren später ihre Lösungen.
  2. Code Katas: Eine Code Kata ist eine kurze, wiederholbare Übung, die ein bestimmtes Problem oder eine Technik behandelt. Sie helfen Entwicklern, ihre Fähigkeiten durch ständige Wiederholung und Verbesserung zu verfeinern. Beispiele sind:

    • FizzBuzz: Eine einfache Übung zur Überprüfung grundlegender Programmierkenntnisse.
    • Roman Numerals: Eine Übung zur Konvertierung von Zahlen in römische Ziffern und umgekehrt.
  3. Pair Programming: Zwei Entwickler arbeiten gemeinsam an einem Computer. Einer schreibt den Code (Driver), während der andere den Code überprüft und navigiert (Navigator). Dies fördert den Wissensaustausch und verbessert die Codequalität.

  4. Refactoring: Regelmäßiges Überarbeiten von Code, um dessen Struktur zu verbessern, ohne die Funktionalität zu ändern. Dies hilft, technischen Schulden vorzubeugen und die Wartbarkeit zu erhöhen.

  5. Test-Driven Development (TDD): Eine Praxis, bei der Tests geschrieben werden, bevor der eigentliche Code implementiert wird. Dies fördert die Erstellung von zuverlässigem und gut getestetem Code.

Zusammenfassung

Das Software Craft Manifesto und die zugehörigen Praktiken und Prinzipien betonen die Bedeutung von handwerklicher Qualität, kontinuierlicher Verbesserung und kollaborativem Lernen in der Softwareentwicklung. Durch Formate wie Coding Dojos, Code Katas und Pair Programming können Entwickler ihre Fähigkeiten kontinuierlich verbessern und eine Kultur der Exzellenz fördern.

Pyramid of Agile Competencies

Die Pyramid of Agile Competencies ist ein Modell, das die Fähigkeiten und Kompetenzen beschreibt, die erforderlich sind, um in einer agilen Umgebung erfolgreich zu arbeiten. Sie besteht aus drei Ebenen, die aufeinander aufbauen und unterschiedliche Aspekte der agilen Arbeitsweise abdecken: Technische Kompetenzen, Kollaborative Kompetenzen und Geschäftsbezogene Kompetenzen.

1. Technische Kompetenzen (Technical Competencies)

Diese bilden die Basis der Pyramide und umfassen die grundlegenden technischen Fähigkeiten und Kenntnisse, die für die agile Softwareentwicklung erforderlich sind. Dazu gehören:

  • Coding Skills: Beherrschung der Programmiersprachen und Technologien, die im Projekt verwendet werden.
  • Automatisiertes Testen: Fähigkeit, automatisierte Tests zu schreiben und zu pflegen, um die Qualität des Codes sicherzustellen.
  • Continuous Integration (CI) und Continuous Deployment (CD): Kenntnis und Anwendung von CI/CD-Praktiken, um den Code kontinuierlich zu integrieren und zu deployen.
  • Refactoring: Fähigkeit, den Code kontinuierlich zu verbessern, um ihn sauber, lesbar und wartbar zu halten.
  • Test-Driven Development (TDD): Praxis, Tests zu schreiben, bevor der eigentliche Code implementiert wird.

Diese technischen Fähigkeiten sind notwendig, um qualitativ hochwertige Software schnell und effizient zu entwickeln.

2. Kollaborative Kompetenzen (Collaborative Competencies)

Diese Ebene betont die Bedeutung der Zusammenarbeit und Kommunikation innerhalb des Teams und mit Stakeholdern. Dazu gehören:

  • Teamarbeit: Fähigkeit, effektiv mit anderen Teammitgliedern zusammenzuarbeiten und gemeinsam Lösungen zu entwickeln.
  • Kommunikation: Klare und effektive Kommunikation innerhalb des Teams und mit externen Stakeholdern.
  • Pair Programming: Praxis, bei der zwei Entwickler gemeinsam an einem Computer arbeiten, um die Qualität des Codes zu verbessern und Wissen zu teilen.
  • Feedback-Kultur: Fähigkeit, konstruktives Feedback zu geben und zu empfangen, um kontinuierlich zu lernen und sich zu verbessern.
  • Retrospektiven: Regelmäßige Meetings, bei denen das Team seine Arbeitsweise reflektiert und Verbesserungsmöglichkeiten identifiziert.

Diese kollaborativen Fähigkeiten fördern ein harmonisches und produktives Arbeitsumfeld, in dem das Team als Einheit agiert.

3. Geschäftsbezogene Kompetenzen (Business Competencies)

Die oberste Ebene der Pyramide konzentriert sich auf das Verständnis der geschäftlichen Anforderungen und den Mehrwert, den die Software liefern soll. Dazu gehören:

  • Produktverständnis: Kenntnis der Produktvision und der Geschäftsziele, die das Team erreichen soll.
  • Kundenzentrierung: Fokussierung auf die Bedürfnisse und Anforderungen der Kunden, um wertvolle und nützliche Software zu entwickeln.
  • Stakeholder-Management: Fähigkeit, effektiv mit Stakeholdern zu kommunizieren und ihre Erwartungen zu managen.
  • Priorisierung: Fähigkeit, Anforderungen und Aufgaben basierend auf ihrem Geschäftswert und ihrer Dringlichkeit zu priorisieren.
  • Agile Planning: Praxis der iterativen Planung und kontinuierlichen Anpassung des Projektplans basierend auf Feedback und Veränderungen.

Diese geschäftsbezogenen Fähigkeiten stellen sicher, dass das Team nicht nur technisch exzellente, sondern auch geschäftlich relevante und wertvolle Lösungen liefert.

Zusammenfassung

Die Pyramid of Agile Competencies zeigt, dass erfolgreiche agile Teams eine Kombination aus technischen, kollaborativen und geschäftsbezogenen Fähigkeiten benötigen. Die Basis der Pyramide bildet die technische Kompetenz, gefolgt von kollaborativen Fähigkeiten und schließlich den geschäftsbezogenen Kompetenzen an der Spitze. Dieses Modell betont die Notwendigkeit einer ausgewogenen Entwicklung in allen drei Bereichen, um in einer agilen Umgebung erfolgreich zu sein.

User Stories

Ziele von User Stories

User Stories sind ein zentrales Element in der agilen Softwareentwicklung. Sie dienen dazu, die Anforderungen aus Sicht des Endbenutzers zu beschreiben und den Fokus auf den Mehrwert für den Benutzer zu legen. Die Hauptziele von User Stories sind:

  1. Kundenzentrierung: Sicherstellung, dass die Entwicklung den tatsächlichen Bedürfnissen der Benutzer entspricht.
  2. Kommunikation: Förderung des Dialogs zwischen Entwicklern, Kunden und anderen Stakeholdern.
  3. Priorisierung: Unterstützung bei der Priorisierung von Aufgaben basierend auf dem Geschäftswert und den Benutzeranforderungen.
  4. Flexibilität: Erleichterung der Anpassung an sich ändernde Anforderungen und Umstände.
  5. Transparenz: Verbesserung der Nachvollziehbarkeit und Klarheit der Anforderungen.

Anwendung von User Stories

User Stories werden während des gesamten Entwicklungsprozesses verwendet, um Anforderungen zu definieren, zu diskutieren und umzusetzen. Sie sind besonders nützlich in agilen Methoden wie Scrum und Kanban. Der typische Workflow umfasst:

  1. Erstellung: Stakeholder und Produktbesitzer schreiben User Stories, um die Anforderungen zu definieren.
  2. Diskussion: Das Team bespricht die Stories, um ein gemeinsames Verständnis zu entwickeln.
  3. Schätzung: Das Team schätzt den Aufwand für die Umsetzung der Stories.
  4. Priorisierung: Stories werden nach ihrem Geschäftswert und ihrer Dringlichkeit priorisiert.
  5. Umsetzung: Das Entwicklungsteam implementiert die Stories während der Sprints oder Iterationen.
  6. Überprüfung: Am Ende des Sprints wird die umgesetzte Story mit dem Kunden oder Produktbesitzer überprüft und abgenommen.

Format einer User Story

Eine User Story folgt einem einfachen und klaren Format, das typischerweise aus drei Teilen besteht:

  1. Rolle (Role): Wer ist der Benutzer oder die Benutzergruppe, für die die Anforderung gilt?
  2. Ziel (Goal): Was möchte der Benutzer erreichen?
  3. Nutzen (Benefit): Warum ist dieses Ziel für den Benutzer wichtig?

Ein gängiges Template für User Stories lautet:

Als [Rolle] möchte ich [Ziel], damit [Nutzen].

Beispiel:

Als Online-Shopper möchte ich Produkte in meinem Warenkorb speichern, damit ich meinen Einkauf später fortsetzen kann.

Zusammenhang zwischen Epics, Themes und Stories

  • Epics: Große, grob umrissene User Stories, die in kleinere Stories unterteilt werden können. Epics sind oft zu groß, um in einem einzigen Sprint abgeschlossen zu werden.
  • Themes: Eine Sammlung von User Stories, die zu einem gemeinsamen übergeordneten Ziel oder Thema gehören. Themes helfen dabei, zusammenhängende Anforderungen zu organisieren und zu verwalten.
  • User Stories: Kleinere, spezifische Anforderungen, die in einem Sprint umgesetzt werden können. Sie sind detaillierter und fokussierter als Epics und Themes.

Beispiel:

  • Theme: Benutzerverwaltung
    • Epic: Benutzerregistrierung
      • User Story: Als neuer Benutzer möchte ich mich registrieren können, damit ich den Service nutzen kann.
      • User Story: Als Benutzer möchte ich mein Passwort zurücksetzen können, damit ich wieder Zugang zu meinem Konto erhalte.

Merkmale einer guten User Story

Eine gute User Story erfüllt die INVEST-Kriterien:

  1. Independent (Unabhängig): Die Story sollte so weit wie möglich unabhängig von anderen Stories sein.
  2. Negotiable (Verhandelbar): Die Details der Story sollten verhandelbar und nicht in Stein gemeißelt sein.
  3. Valuable (Wertvoll): Die Story sollte für den Endbenutzer oder den Kunden einen klaren Mehrwert bieten.
  4. Estimable (Schätzbar): Das Team sollte in der Lage sein, den Aufwand für die Umsetzung der Story zu schätzen.
  5. Small (Klein): Die Story sollte klein genug sein, um in einem Sprint umgesetzt werden zu können.
  6. Testable (Testbar): Es sollte klar sein, wie die erfolgreiche Umsetzung der Story überprüft werden kann.

Beispiel für eine gute User Story:

Als registrierter Benutzer möchte ich eine Liste meiner letzten Bestellungen sehen, damit ich schnell auf meine vergangenen Käufe zugreifen kann.
  • Independent: Kann unabhängig von anderen Features implementiert werden.
  • Negotiable: Details können diskutiert und angepasst werden.
  • Valuable: Bietet dem Benutzer einen klaren Nutzen.
  • Estimable: Aufwand kann geschätzt werden.
  • Small: Kann innerhalb eines Sprints abgeschlossen werden.
  • Testable: Erfolgskriterien sind klar definierbar.

Zusammenfassung

User Stories sind ein wesentlicher Bestandteil der agilen Softwareentwicklung, um die Anforderungen klar, fokussiert und benutzerorientiert zu definieren. Durch die Strukturierung in Epics, Themes und Stories können komplexe Anforderungen organisiert und verwaltet werden. Gute User Stories zeichnen sich durch die Erfüllung der INVEST-Kriterien aus, was zu einer effektiven und effizienten Umsetzung führt.

Estimation and Planning in Agile

User Stories

User Stories sind kurze, einfache Beschreibungen einer Funktionalität aus der Perspektive des Endbenutzers. Sie folgen typischerweise dem Format: "Als [Rolle] möchte ich [Ziel], damit [Nutzen]."

User Roles

User Roles definieren verschiedene Benutzertypen, die mit dem System interagieren werden. Diese Rollen helfen, die Anforderungen und Erwartungen unterschiedlicher Benutzergruppen zu berücksichtigen.

Epics

Epics sind große User Stories, die zu umfangreich sind, um in einem einzigen Sprint umgesetzt zu werden. Sie werden in kleinere, handhabbare User Stories aufgeteilt.

Themes

Themes sind Sammlungen von User Stories, die einem gemeinsamen übergeordneten Ziel oder Thema dienen. Sie helfen bei der Organisation und Verwaltung zusammenhängender Anforderungen.

Story Points

Story Points sind eine Einheit zur Schätzung des relativen Aufwands zur Umsetzung einer User Story. Sie basieren auf der Komplexität, dem Risiko und der Unsicherheit der Aufgabe.

Velocity

Velocity ist die Geschwindigkeit, mit der ein Team User Stories in einem Sprint abschließt. Sie wird typischerweise als die Summe der Story Points der abgeschlossenen Stories gemessen.

Planning Poker

Planning Poker ist eine Methode zur Schätzung von User Stories, bei der Teammitglieder Karten mit verschiedenen Schätzungen (in Story Points) verdeckt auswählen und dann gleichzeitig aufdecken. Diskussionen folgen, um eine gemeinsame Schätzung zu erreichen.

Conditions of Satisfaction

Conditions of Satisfaction sind Kriterien, die definieren, wann eine User Story als vollständig und erfolgreich umgesetzt gilt. Sie helfen, die Erwartungen klar zu definieren und sicherzustellen, dass die Anforderungen erfüllt werden.

Levels of Planning

Die Planung in agilen Projekten erfolgt auf mehreren Ebenen:

  • Vision und Roadmap: Langfristige Ziele und strategische Ausrichtung des Produkts.
  • Release Planning: Planung von Releases mit mehreren Sprints, um spezifische Ziele zu erreichen.
  • Sprint Planning: Detaillierte Planung für einen einzelnen Sprint, basierend auf der Priorität der User Stories und der verfügbaren Velocity.

Product Backlog

Der Product Backlog ist eine priorisierte Liste von Anforderungen (User Stories, Epics, etc.), die für das Produkt entwickelt werden sollen. Der Product Owner ist verantwortlich für die Pflege und Priorisierung des Backlogs.

Priorisierung (der User Stories)

User Stories werden basierend auf ihrem Geschäftswert, Risiko, Aufwand und anderen Faktoren priorisiert. Methoden zur Priorisierung umfassen MoSCoW (Must have, Should have, Could have, Won't have), Kano-Modell und Wert-Risiko-Matrix.

Techniques for Estimating

Zu den Schätztechniken gehören:

  • Planning Poker: Kartenbasierte Schätzmethode.
  • T-Shirt Sizes: Grobe Schätzungen in Kategorien wie S, M, L, XL.
  • Affinity Estimation: Gruppieren von User Stories nach ähnlichem Aufwand.

Relative Schätzung

Relative Schätzung bedeutet, den Aufwand von User Stories im Verhältnis zueinander zu bewerten, anstatt absolute Zeitangaben zu machen. Dies erleichtert die Schätzung und Anpassung bei Änderungen.

Planning for Value, Cost, Financial Value, Risk, New Knowledge

Planning for Value

Planung, um den größtmöglichen Geschäftswert zu liefern, indem man sich auf die wichtigsten und wertvollsten Features konzentriert.

Cost

Kosten umfassen alle Ressourcen, die für die Entwicklung und Lieferung der Software erforderlich sind, einschließlich Zeit, Personal und Infrastruktur.

Financial Value

Der finanzielle Wert misst den monetären Nutzen, den die Software für das Unternehmen generiert, z.B. durch Einnahmensteigerungen oder Kosteneinsparungen.

Risk

Risiko bezieht sich auf die Unsicherheiten und potenziellen Probleme, die den Projekterfolg gefährden könnten. Agiles Risikomanagement versucht, Risiken frühzeitig zu identifizieren und zu mindern.

New Knowledge

Neue Erkenntnisse oder Entdeckungen, die während des Projekts gemacht werden und die Planung und Priorisierung beeinflussen können.

Kano-Model of Customer Satisfaction

Das Kano-Modell klassifiziert Kundenanforderungen in drei Kategorien:

  • Basismerkmale: Muss-Kriterien, die selbstverständlich erwartet werden.
  • Leistungsmerkmale: Kriterien, die direkt mit der Kundenzufriedenheit korrelieren.
  • Begeisterungsmerkmale: Unerwartete, aber erfreuliche Features, die Kunden begeistern.

Planungsebenen im agilen Kontext und Zuständigkeiten

  1. Vision und Roadmap

    • Verantwortlich: Produktbesitzer und Führungskräfte.
    • Beschreibung: Langfristige strategische Planung und Zielsetzung des Produkts.
  2. Release Planning

    • Verantwortlich: Produktbesitzer, Scrum Master und Team.
    • Beschreibung: Planung von Releases über mehrere Sprints, um spezifische Meilensteine zu erreichen.
  3. Sprint Planning

    • Verantwortlich: Scrum Master und Entwicklungsteam.
    • Beschreibung: Detaillierte Planung eines einzelnen Sprints, basierend auf priorisierten User Stories und der Team-Velocity.

Insgesamt ermöglicht die agile Planung eine flexible und iterative Herangehensweise, die darauf abzielt, kontinuierlich wertvolle und qualitativ hochwertige Software zu liefern, die den sich ändernden Anforderungen der Benutzer entspricht.

Build Automation, CI, CD, DevOps

Arten der Software Automation

Software Automation umfasst verschiedene Arten, die den Entwicklungsprozess effizienter gestalten:

  1. On-Demand Automation:

    • Beschreibung: Aufgaben werden manuell vom Entwickler oder einem Teammitglied ausgelöst.
    • Beispiel: Manuelles Ausführen eines Builds oder Tests, wenn ein Entwickler dies für notwendig hält.
  2. Scheduled Automation:

    • Beschreibung: Aufgaben werden zu festgelegten Zeiten oder Intervallen automatisch ausgeführt.
    • Beispiel: Nächtliche Builds oder wöchentliche Integrations-Tests.
  3. Triggered Automation:

    • Beschreibung: Aufgaben werden automatisch durch bestimmte Ereignisse oder Bedingungen ausgelöst.
    • Beispiel: Automatischer Build und Testlauf bei jedem Code-Commit oder Merge in das Haupt-Repository.

Ziele der Software Automation

  1. Effizienzsteigerung: Reduzierung manueller, repetitiver Aufgaben, um Zeit und Ressourcen zu sparen.
  2. Fehlerreduktion: Minimierung menschlicher Fehler durch konsistente und reproduzierbare Prozesse.
  3. Schnellere Feedback-Zyklen: Schnellere Erkennung und Behebung von Fehlern durch kontinuierliche Integration und Tests.
  4. Kontinuierliche Bereitstellung: Ermöglichen häufigerer und zuverlässigerer Software-Releases.
  5. Verbesserte Qualität: Durchgängige Tests und Überprüfungen gewährleisten eine höhere Codequalität.

Arten der Automation

  1. Build Automation: Automatisierung des Kompilierens und Erstellens der Software, einschließlich Abhängigkeitsmanagement und Versionskontrolle.
  2. Test Automation: Automatisierung von Tests, um sicherzustellen, dass neue Änderungen die bestehende Funktionalität nicht beeinträchtigen.
  3. Deployment Automation: Automatisierung des Bereitstellens der Software auf verschiedenen Umgebungen (z.B. Test, Staging, Produktion).
  4. Monitoring Automation: Automatisierung der Überwachung der Software und Infrastruktur, um Fehler und Leistungsprobleme frühzeitig zu erkennen.

Software Automation Pipeline

Die Software Automation Pipeline, oft auch CI/CD-Pipeline genannt, umfasst mehrere Schritte, die darauf abzielen, den Entwicklungsprozess effizient und zuverlässig zu gestalten:

  1. Code:

    • Beschreibung: Entwickeln und Verwalten des Quellcodes in einem Versionskontrollsystem wie Git.
    • Tools: Git, Bitbucket, GitHub.
  2. Build:

    • Beschreibung: Kompilieren des Codes, Verwalten von Abhängigkeiten und Erstellen von ausführbaren Artefakten.
    • Tools: Maven, Gradle, Ant.
  3. Test:

    • Beschreibung: Automatisierte Tests, einschließlich Unit-Tests, Integrationstests und Akzeptanztests.
    • Tools: JUnit, Selenium, TestNG.
  4. Package:

    • Beschreibung: Verpacken der ausführbaren Artefakte in Formaten wie JAR, WAR, Docker-Images.
    • Tools: Docker, Kubernetes.
  5. Release:

    • Beschreibung: Verwalten und Veröffentlichen der fertigen Artefakte in Repositories oder Bereitstellungsplattformen.
    • Tools: Nexus, Artifactory.
  6. Deploy:

    • Beschreibung: Bereitstellen der Software in verschiedenen Umgebungen wie Test, Staging und Produktion.
    • Tools: Jenkins, Ansible, Chef, Puppet.
  7. Operate:

    • Beschreibung: Betrieb und Überwachung der Software in der Produktionsumgebung, einschließlich Skalierung und Wartung.
    • Tools: Nagios, Prometheus, Grafana.
  8. Monitor:

    • Beschreibung: Kontinuierliche Überwachung der Softwareleistung und Verfügbarkeit, um Probleme frühzeitig zu erkennen und zu beheben.
    • Tools: ELK Stack (Elasticsearch, Logstash, Kibana), Splunk.

Unterschiede in den Schritten der Pipeline

  • Build vs. Package: Der Build-Schritt konzentriert sich auf das Kompilieren des Codes und das Erstellen von Binärdateien, während der Package-Schritt die Erstellung von Deployment-Artefakten umfasst, die in verschiedenen Umgebungen verwendet werden können.
  • Deploy vs. Release: Deployment bezieht sich auf das physische Bereitstellen der Software in einer Umgebung, während Release den Prozess der Freigabe einer Version für die Produktion beschreibt.
  • Test: Kann mehrere Phasen haben, wie Unit-Tests, Integrationstests, Systemtests und Akzeptanztests, die jeweils unterschiedliche Aspekte der Software überprüfen.

Zusammenfassung

Build Automation, Continuous Integration (CI), Continuous Deployment (CD) und DevOps zielen darauf ab, den Softwareentwicklungsprozess durch Automatisierung effizienter und zuverlässiger zu gestalten. Die Pipeline der Software Automation umfasst mehrere Schritte, die von der Code-Entwicklung über das Testen und Paketieren bis hin zum Deployment und Monitoring reichen. Diese Automatisierung hilft, die Qualität der Software zu verbessern, die Entwicklungszyklen zu verkürzen und schneller auf Änderungen und Anforderungen zu reagieren.

Scrum

Scrum ist ein Rahmenwerk für agile Projektmanagement und Softwareentwicklung, das iterative und inkrementelle Ansätze nutzt, um komplexe Projekte zu bewältigen. Hier sind die Hauptcharakteristiken von Scrum, einschließlich Rollen, Zeremonien, Artefakten und Werten:

Scrum Rollen

  1. Product Owner:

    • Verantwortlich für das Produkt-Backlog und die Maximierung des Werts des Produkts.
    • Stellt sicher, dass das Team die richtigen Aufgaben priorisiert und erledigt.
    • Verbindet die Interessen der Stakeholder mit dem Entwicklungsteam.
  2. Scrum Master:

    • Verantwortlich für die Einhaltung der Scrum-Prinzipien und -Praktiken.
    • Unterstützt das Team, Hindernisse zu beseitigen und effizient zu arbeiten.
    • Moderiert die Zeremonien und schützt das Team vor äußeren Störungen.
  3. Entwicklungsteam:

    • Selbstorganisierende und funktionsübergreifende Gruppe von Fachleuten.
    • Verantwortlich für die Umsetzung der Sprint-Ziele und die Lieferung inkrementeller Produkte.
    • Besteht normalerweise aus 3-9 Mitgliedern.

Scrum Zeremonien

  1. Sprint Planning:

    • Findet zu Beginn jedes Sprints statt.
    • Ziel: Planung der Arbeit für den kommenden Sprint und Festlegung des Sprint-Ziels.
    • Das Entwicklungsteam wählt Items aus dem Product Backlog, die es innerhalb des Sprints abschließen kann.
  2. Daily Scrum (Stand-up):

    • Tägliches 15-minütiges Meeting.
    • Ziel: Teammitglieder berichten, was sie seit dem letzten Meeting getan haben, was sie als nächstes tun werden und welche Hindernisse es gibt.
    • Fördert die Zusammenarbeit und Transparenz im Team.
  3. Sprint Review:

    • Am Ende des Sprints.
    • Ziel: Vorstellung der während des Sprints abgeschlossenen Arbeit.
    • Feedback von Stakeholdern einholen und das Produkt-Backlog entsprechend anpassen.
  4. Sprint Retrospective:

    • Nach dem Sprint Review.
    • Ziel: Reflexion über den vergangenen Sprint und Identifikation von Verbesserungsmöglichkeiten.
    • Das Team diskutiert, was gut gelaufen ist, was verbessert werden kann und wie die Verbesserungen umgesetzt werden sollen.

Scrum Artefakte

  1. Product Backlog:

    • Eine priorisierte Liste von Anforderungen (User Stories, Aufgaben), die für das Produkt notwendig sind.
    • Wird vom Product Owner gepflegt und regelmäßig aktualisiert.
  2. Sprint Backlog:

    • Eine Liste von Aufgaben, die das Team während des Sprints umsetzen möchte.
    • Wird während des Sprint Planning Meetings erstellt.
  3. Increment:

    • Das kumulierte Ergebnis aller abgeschlossenen Product Backlog Items eines Sprints.
    • Muss den Definition of Done (DoD) entsprechen und in einem nutzbaren Zustand sein.

Scrum Werte

  1. Commitment (Verpflichtung):

    • Das Team verpflichtet sich, die Ziele des Sprints zu erreichen.
  2. Courage (Mut):

    • Teammitglieder haben den Mut, schwierige Entscheidungen zu treffen und Herausforderungen anzunehmen.
  3. Focus (Fokus):

    • Das Team konzentriert sich auf die Arbeit im Sprint und die Erreichung des Sprint-Ziels.
  4. Openness (Offenheit):

    • Es herrscht eine offene Kommunikation über Fortschritte, Hindernisse und Verbesserungsmöglichkeiten.
  5. Respect (Respekt):

    • Teammitglieder respektieren einander und schätzen die Fähigkeiten und Beiträge jedes Einzelnen.

Begriffe in Scrum

  1. Sprint:

    • Eine zeitlich festgelegte Iteration von typischerweise 1-4 Wochen, in der ein inkrementelles Produkt geliefert wird.
  2. Sprint Goal:

    • Ein spezifisches Ziel, das während des Sprints erreicht werden soll, und das dem Team einen gemeinsamen Fokus gibt.
  3. Retrospective:

    • Eine Zeremonie am Ende eines Sprints, bei der das Team reflektiert und Verbesserungsmöglichkeiten identifiziert.
  4. Task Board:

    • Ein visuelles Tool zur Verfolgung des Fortschritts der Aufgaben im Sprint. Es zeigt typischerweise Spalten wie "To Do", "In Progress" und "Done".
  5. Burndown Chart:

    • Ein Diagramm, das den verbleibenden Arbeitsaufwand im Sprint im Zeitverlauf darstellt und zeigt, ob das Team auf dem richtigen Weg ist, um die Sprint-Ziele zu erreichen.
  6. Definition of Done (DoD):

    • Eine klare Liste von Kriterien, die erfüllt sein müssen, damit eine User Story als vollständig betrachtet wird.
  7. Definition of Ready (DoR):

    • Eine Liste von Kriterien, die erfüllt sein müssen, damit eine User Story als bereit für die Bearbeitung im Sprint betrachtet wird.
  8. Daily Scrum:

    • Ein tägliches, 15-minütiges Meeting, in dem Teammitglieder ihren Fortschritt besprechen, bevorstehende Aufgaben planen und Hindernisse identifizieren.
  9. Increment:

    • Das Ergebnis eines Sprints, das den Definition of Done-Kriterien entspricht und ein nutzbares, potentiell auslieferbares Produkt darstellt.

Zusammenfassung

Scrum ist ein agiles Framework, das auf kollaborative, iterative und inkrementelle Ansätze setzt, um komplexe Projekte zu bewältigen. Es fördert Transparenz, Inspektion und Anpassung durch regelmäßige Zeremonien und klare Rollen. Die Werte von Scrum legen den Grundstein für eine respektvolle, fokussierte und mutige Arbeitsweise, die darauf abzielt, kontinuierlich wertvolle und qualitativ hochwertige Produkte zu liefern.

Architektur Patterns

CQRS (Command Query Responsibility Segregation)

CQRS ist ein Architekturpattern, das die Lese- und Schreiboperationen eines Systems trennt. Dies bedeutet, dass es unterschiedliche Modelle für Abfragen (Queries) und Befehle (Commands) gibt.

  • Vorteile:

    • Optimierte Lese- und Schreibmodelle.
    • Bessere Skalierbarkeit und Leistung.
    • Erleichtert die Implementierung von Event Sourcing.
  • Nachteile:

    • Komplexität der Implementierung.
    • Dateninkonsistenz zwischen Lese- und Schreibmodell möglich.

Event Sourcing

Event Sourcing ist ein Architekturpattern, bei dem der Zustand eines Systems durch eine Abfolge von Ereignissen (Events) gespeichert wird, anstatt den aktuellen Zustand direkt zu speichern.

  • Vorteile:

    • Volle Nachvollziehbarkeit und Reproduzierbarkeit von Änderungen.
    • Unterstützung für Audit- und Wiederherstellungsprozesse.
    • Erleichtert die Implementierung von CQRS.
  • Nachteile:

    • Komplexität der Event-Nachverfolgung und -Wartung.
    • Erfordert robuste Event-Handling-Mechanismen.

Strangler Pattern

Das Strangler Pattern ist eine Methode zur schrittweisen Migration von Altsystemen zu neuen Systemen. Neue Funktionen werden in ein neues System geschrieben, während das alte System schrittweise zurückgebaut wird.

  • Vorteile:

    • Minimierung der Risiken bei der Migration.
    • Kontinuierliche Verbesserungen und Refactoring möglich.
    • Keine Downtime erforderlich.
  • Nachteile:

    • Komplexität der Koexistenz von Altsystem und neuem System.
    • Zusätzlicher Integrationsaufwand.

Online-Migration

Online-Migration ist der Prozess der Datenmigration, bei dem das System während der Migration online und verfügbar bleibt.

  • Vorteile:

    • Minimale Ausfallzeiten.
    • Bessere Benutzererfahrung durch kontinuierliche Verfügbarkeit.
  • Nachteile:

    • Erhöhte Komplexität und Risiko der Dateninkonsistenz.
    • Erfordert sorgfältige Planung und Testing.

Circuit Breaker

Das Circuit Breaker Pattern schützt eine Anwendung vor Kaskadeneffekten von Fehlern und Überlastungen in abhängigen Diensten. Es unterbricht Aufrufe zu fehlerhaften Diensten und stellt nach einer bestimmten Zeitspanne Verbindungen wieder her.

  • Vorteile:

    • Verbesserung der Systemstabilität und Fehlertoleranz.
    • Verhindert Ressourcenerschöpfung durch wiederholte Fehlversuche.
  • Nachteile:

    • Komplexität der Implementierung und Konfiguration.
    • Mögliche Verzögerungen durch falsche Auslösungen.

Bulkhead (Schottwand)

Das Bulkhead Pattern teilt ein System in unabhängige Bereiche (Bulkheads), um den Einfluss eines Fehlers zu begrenzen und die Gesamtsystemstabilität zu erhöhen.

  • Vorteile:

    • Verbesserung der Fehlertoleranz und Isolation.
    • Reduzierung der Auswirkungen eines Teilausfalls.
  • Nachteile:

    • Zusätzlicher Verwaltungsaufwand für die Konfiguration und Wartung.
    • Mögliche Ressourcenunterauslastung.

Retry

Das Retry Pattern wiederholt fehlgeschlagene Operationen nach einer definierten Anzahl von Versuchen und Zeitintervallen, um vorübergehende Fehler zu überwinden.

  • Vorteile:

    • Erhöhung der Erfolgsrate von Operationen.
    • Vermeidung unnötiger Fehlermeldungen durch vorübergehende Probleme.
  • Nachteile:

    • Erhöhung der Latenzzeit bei wiederholten Versuchen.
    • Risiko von Überlastungen durch wiederholte Versuche.

Serverless

Serverless ist ein Architekturmuster, bei dem Anwendungen auf verwalteten Cloud-Diensten laufen, die automatisch skalieren und Ressourcen basierend auf der tatsächlichen Nutzung bereitstellen.

  • Vorteile:

    • Keine Serververwaltung erforderlich.
    • Automatische Skalierung und hohe Verfügbarkeit.
    • Bezahlung nach Nutzung.
  • Nachteile:

    • Begrenzte Kontrolle über die Infrastruktur.
    • Potenzielle Abhängigkeit von Cloud-Anbietern.

Microservices

Microservices ist ein Architekturmuster, bei dem eine Anwendung aus einer Sammlung kleiner, unabhängiger Dienste besteht, die jeweils einen spezifischen Geschäftsfähigkeitsbereich abdecken.

  • Vorteile:

    • Unabhängige Entwicklung, Bereitstellung und Skalierung von Diensten.
    • Erhöhte Flexibilität und Agilität.
    • Bessere Fehlertoleranz und Isolation.
  • Nachteile:

    • Komplexität des Service-Managements und der Kommunikation.
    • Herausforderungen bei der Datenkonsistenz und -integration.

Self-contained Systems

Self-contained Systems (SCS) ist ein Architekturansatz, bei dem eine Anwendung in unabhängige Systeme unterteilt wird, die jeweils eine spezifische Domäne abdecken und sowohl Frontend- als auch Backend-Funktionen enthalten.

  • Vorteile:

    • Unabhängige Entwicklung und Bereitstellung.
    • Reduzierung der Abhängigkeiten zwischen Teams und Systemen.
    • Bessere Skalierbarkeit und Wartbarkeit.
  • Nachteile:

    • Erhöhte Komplexität der Systemintegration.
    • Herausforderungen bei der Konsistenz und gemeinsamen Nutzung von Daten.

Monolith (Modulith)

Ein Monolith ist eine traditionelle Architekturmuster, bei dem eine Anwendung als eine einzige, zusammenhängende Einheit entwickelt und bereitgestellt wird. Ein Modulith ist ein gut strukturierter Monolith, der in gut definierte Module unterteilt ist.

  • Vorteile:

    • Einfache Entwicklung und Bereitstellung.
    • Keine Netzwerk-Latenz zwischen Modulen.
    • Einfacheres Debugging und Testing.
  • Nachteile:

    • Schwierigkeiten bei der Skalierung und Wartung großer Codebasen.
    • Abhängigkeiten zwischen Modulen können Änderungen erschweren.
    • Begrenzte Flexibilität und Agilität.

Zusammenfassung

Architekturpatterns bieten verschiedene Ansätze zur Strukturierung und Verwaltung von Softwareanwendungen, um spezifische Probleme und Herausforderungen zu lösen. Die Wahl des richtigen Patterns hängt von den spezifischen Anforderungen, Zielen und Kontexten der Anwendung ab.

Software Architecture

Begriffe und Erklärungen

Software-Architektur Die Software-Architektur beschreibt die grundlegenden Strukturen einer Softwareanwendung, einschließlich der Komponenten und deren Beziehungen zueinander. Sie umfasst die Entwurfsentscheidungen, die getroffen werden, um die funktionalen und nicht-funktionalen Anforderungen zu erfüllen.

Software-Architekt Ein Software-Architekt ist verantwortlich für die Gestaltung und das Design der Software-Architektur. Dies beinhaltet die Entscheidung über die Struktur der Software, die Auswahl der Technologien und die Definition von Standards und Prinzipien, die das Entwicklungsteam befolgen soll.

Unterschied zwischen Enterprise Architecture und Application Architecture

  • Enterprise Architecture: Umfasst die gesamte IT-Infrastruktur einer Organisation, einschließlich der Strategien, Prinzipien und Richtlinien, die zur Verwaltung der IT-Ressourcen verwendet werden. Sie zielt darauf ab, die IT-Umgebung mit den Geschäftszielen der Organisation in Einklang zu bringen.
  • Application Architecture: Bezieht sich auf das Design und die Struktur einer einzelnen Softwareanwendung. Sie konzentriert sich auf die internen Komponenten der Anwendung und deren Beziehungen sowie auf die Integration mit anderen Systemen.

Schwierigkeiten des Software Designs

  1. Complexity (Komplexität):

    • Software-Systeme sind oft sehr komplex, was das Verständnis, die Entwicklung und die Wartung erschwert.
  2. Conformity (Konformität):

    • Software muss sich an externe Standards, Schnittstellen und Systeme anpassen, was die Flexibilität einschränkt und die Komplexität erhöht.
  3. Changeability (Änderbarkeit):

    • Anforderungen und Technologien ändern sich ständig, was die kontinuierliche Anpassung der Software erforderlich macht.
  4. Invisibility (Unsichtbarkeit):

    • Software ist immateriell und ihre Struktur ist oft schwer zu visualisieren, was die Kommunikation und das Verständnis erschwert.

Architectural Drivers

  1. Functional Requirements (Funktionale Anforderungen):

    • Beschreiben, was das System tun soll. Diese Anforderungen bestimmen die Funktionen und Features, die das System bieten muss.
  2. Non-Functional Requirements (Nicht-funktionale Anforderungen):

    • Beziehen sich auf die Qualitätseigenschaften des Systems, wie Leistung, Sicherheit, Zuverlässigkeit und Wartbarkeit. Diese Anforderungen beeinflussen die Architekturentscheidungen maßgeblich.

Begriffe und Unterschiede

Standard:

  • Ein definierter und anerkannter Weg zur Durchführung einer Aufgabe oder zur Gestaltung eines Systems. Standards bieten Konsistenz und Interoperabilität.

Style (Architekturstil):

  • Ein Satz von Prinzipien, der zur Strukturierung und Organisation einer Softwarearchitektur verwendet wird. Beispiele sind Layered Architecture, Microservices und Event-Driven Architecture.

Pattern (Architekturpattern):

  • Wiederverwendbare Lösung für ein häufig auftretendes Problem in einem bestimmten Kontext. Beispiele sind Singleton, Factory und Observer.

Layer vs. Tier:

  • Layer (Schicht): Eine logische Trennung von Verantwortlichkeiten innerhalb einer Softwareanwendung. Beispiel: Präsentationsschicht, Geschäftsschicht, Datenschicht.
  • Tier (Ebene): Eine physische Trennung von Komponenten, die auf unterschiedlichen Maschinen oder Servern ausgeführt werden. Beispiel: Client-Server-Architektur, bei der der Client auf einer Maschine und der Server auf einer anderen Maschine läuft.

Architekturstile

Layered Architecture

  • Vorteile: Einfach zu verstehen, gut strukturiert, ermöglicht die Trennung von Verantwortlichkeiten.
  • Nachteile: Kann zu Leistungseinbußen führen, schwer zu ändern, wenn Schichten stark gekoppelt sind.

Microservices Architecture

  • Vorteile: Skalierbarkeit, Flexibilität, Unabhängigkeit der Dienste, einfache Wartbarkeit.
  • Nachteile: Komplexität der Verwaltung, erhöhtes Risiko der Netzwerkkommunikation, Datenkonsistenzprobleme.

Event-Driven Architecture

  • Vorteile: Entkopplung von Komponenten, Skalierbarkeit, Echtzeitverarbeitung.
  • Nachteile: Komplexität der Ereignisverwaltung, schwierige Fehlerbehandlung, Potenzial für inkonsistente Datenzustände.

Monolithic Architecture

  • Vorteile: Einfacher zu entwickeln und bereitzustellen, weniger Überkopfkosten durch weniger Netzwerkkommunikation.
  • Nachteile: Schwer skalierbar, schwierig zu warten und zu erweitern, potenziell längere Entwicklungszeiten bei Änderungen.

Drei große Patterns

  1. Transaction Script:

    • Einsatzgebiet: Einfachere Anwendungen, bei denen Geschäftslogik direkt in Prozeduren oder Skripten implementiert wird.
    • Vorteile: Einfachheit, schnelle Entwicklung.
    • Nachteile: Schwer wartbar und erweiterbar bei wachsender Komplexität.
  2. Domain Model:

    • Einsatzgebiet: Komplexere Anwendungen mit umfangreicher Geschäftslogik.
    • Vorteile: Bessere Wartbarkeit, Flexibilität, gute Abbildung von Geschäftsregeln.
    • Nachteile: Höhere Komplexität und Lernkurve.
  3. Table Module:

    • Einsatzgebiet: Anwendungen, bei denen die Geschäftslogik eng mit der Datenbankstruktur verbunden ist.
    • Vorteile: Strukturierte Abbildung der Geschäftslogik auf Tabellenebene.
    • Nachteile: Kann zu einer engen Kopplung zwischen Geschäftslogik und Datenbank führen.

C4-Modell

Das C4-Modell bietet eine einfache Möglichkeit, Softwarearchitekturen auf vier Ebenen zu visualisieren:

  1. Context:
    • Überblick über das System und seine Umgebung.
  2. Container:
    • Darstellung der verschiedenen Container (Applikationen, Datenbanken, etc.) im System.
  3. Component:
    • Details der Hauptkomponenten innerhalb eines Containers.
  4. Code:
    • Details auf der Code-Ebene (Klassen, Methoden).

Architecture Canvas

Vorteile:

  • Ermöglicht eine strukturierte Visualisierung und Planung von Architekturentscheidungen.
  • Fördert die Zusammenarbeit und Kommunikation zwischen Teams.
  • Bietet eine klare Übersicht über die wichtigsten Architekturkomponenten und -abhängigkeiten.

Nachteile:

  • Kann bei großen Systemen unübersichtlich werden.
  • Erfordert regelmäßige Aktualisierung und Pflege.

Einsatz:

  • Wird verwendet, um die Architektur eines Systems zu planen, zu dokumentieren und zu kommunizieren.

Zusammenfassung

Software-Architektur umfasst die grundlegenden Strukturen und Prinzipien, die zur Gestaltung und Verwaltung von Softwareanwendungen verwendet werden. Verschiedene Architekturstile und Patterns bieten spezifische Vorteile und Herausforderungen, die je nach Anwendungskontext berücksichtigt werden müssen. Das C4-Modell und das Architecture Canvas sind wertvolle Werkzeuge zur Visualisierung und Kommunikation von Architekturentscheidungen.

Cynefin Framework

Nutzen von Cynefin im Kontext der Softwareentwicklung

Das Cynefin Framework bietet einen konzeptionellen Rahmen, um Probleme und Entscheidungssituationen zu kategorisieren und zu verstehen. Es hilft Teams, die Natur ihrer Herausforderungen besser einzuschätzen und entsprechende Lösungsansätze zu wählen. Im Kontext der Softwareentwicklung ermöglicht Cynefin:

  • Bessere Problemerkennung: Unterscheidung zwischen verschiedenen Arten von Problemen, um geeignete Ansätze und Methoden auszuwählen.
  • Anpassungsfähigkeit: Förderung von Flexibilität und Anpassungsfähigkeit in der Herangehensweise an Projekte und Probleme.
  • Effiziente Ressourcennutzung: Vermeidung von Verschwendung durch den Einsatz passender Techniken und Ressourcen für verschiedene Problemarten.

Begriffe und Erklärungen

Cynefin:

  • Ein konzeptionelles Framework, das von Dave Snowden entwickelt wurde, um Organisationen zu helfen, Kontext zu schaffen, Entscheidungen zu treffen und Probleme zu lösen, indem es fünf verschiedene Domänen zur Klassifizierung von Problemen und Entscheidungssituationen bietet.

Framework:

  • Ein strukturiertes Modell oder System, das als Grundlage dient, um Ideen zu organisieren, Entscheidungen zu treffen und Probleme zu analysieren.

Exaptation:

  • Der Prozess, bei dem eine Funktion oder Fähigkeit, die ursprünglich für einen bestimmten Zweck entwickelt wurde, für einen neuen, oft unvorhergesehenen Zweck verwendet wird.

4 + 1 Domains des Cynefin Frameworks

  1. Simple (Eindeutig):

    • Beschreibung: Klar definierte Probleme mit bekannten Lösungen.
    • Vorgehen: Best Practices anwenden. Probleme erkennen, kategorisieren und reagieren.
    • Beispiel: Standardisierte Software-Installationen.
  2. Complicated (Kompliziert):

    • Beschreibung: Probleme, die analysiert und von Experten gelöst werden können.
    • Vorgehen: Good Practices anwenden. Analysieren, Experten hinzuziehen und reagieren.
    • Beispiel: Architekturdesign für eine komplexe, aber bekannte Anwendung.
  3. Complex (Komplex):

    • Beschreibung: Probleme mit unbekannten Lösungen, die durch Probe und Irrtum entdeckt werden müssen.
    • Vorgehen: Emergent Practices anwenden. Probieren, Muster erkennen und reagieren.
    • Beispiel: Entwicklung eines innovativen Produkts in einem neuen Markt.
  4. Chaotic (Chaotisch):

    • Beschreibung: Probleme, die sofortiges Handeln erfordern, ohne Zeit für Analyse.
    • Vorgehen: Novel Practices anwenden. Handeln, Stabilität herstellen und dann analysieren.
    • Beispiel: Krisenmanagement nach einem unerwarteten Systemausfall.
  5. Disorder (Unordnung):

    • Beschreibung: Zustand, in dem nicht klar ist, welche der anderen Domänen zutrifft.
    • Vorgehen: Ziel ist es, die Situation in eine der vier anderen Domänen einzuordnen, um geeignete Maßnahmen zu ergreifen.

Weitere Begriffe

Causality (Kausalität):

  • Das Prinzip, dass jede Wirkung eine Ursache hat. In einfachen und komplizierten Domänen sind Ursache und Wirkung klar erkennbar, in komplexen und chaotischen Domänen nicht.

Correlation (Korrelation):

  • Eine Beziehung zwischen zwei Variablen, die miteinander in Verbindung stehen, aber nicht notwendigerweise eine Kausalität implizieren.

Constraint (Einschränkung):

  • Regeln oder Grenzen, die das Verhalten innerhalb einer Domäne beeinflussen. In verschiedenen Domänen können die Art und der Grad der Einschränkungen variieren.

Codefin Framework

Praktische Anwendung in der Softwareentwicklung

Das Codefin Framework ist eine spezialisierte Anwendung des Cynefin Frameworks in der Softwareentwicklung. Es hilft Entwicklern, verschiedene Arten von Softwareprojekten und -problemen zu identifizieren und die besten Ansätze zur Bewältigung dieser Probleme zu wählen.

  1. Simple Projekte:

    • Beispiel: Regelmäßige Wartungsaufgaben oder kleine Updates.
    • Ansatz: Anwendung bewährter Methoden und Automatisierung.
  2. Complicated Projekte:

    • Beispiel: Entwicklung eines Moduls für eine bekannte Anwendung.
    • Ansatz: Einbindung von Experten, Durchführung detaillierter Analysen und Planung.
  3. Complex Projekte:

    • Beispiel: Entwicklung eines neuen, innovativen Produkts oder Features.
    • Ansatz: Agile Methoden anwenden, iterativ vorgehen, Experimente durchführen und regelmäßig Feedback einholen.
  4. Chaotic Situationen:

    • Beispiel: Krisenmanagement bei schwerwiegenden Systemausfällen.
    • Ansatz: Sofortige Maßnahmen ergreifen, um das System zu stabilisieren, und anschließend die Ursachen analysieren.
  5. Disorder:

    • Ansatz: Die Situation analysieren und versuchen, sie einer der anderen Domänen zuzuordnen, um geeignete Maßnahmen zu ergreifen.

Zusammenfassung

Das Cynefin Framework bietet eine strukturierte Methode zur Klassifizierung von Problemen und Entscheidungssituationen, die es Teams in der Softwareentwicklung ermöglicht, geeignete Ansätze zur Problemlösung zu wählen. Durch die Anwendung des Codefin Frameworks können Entwickler spezifische Softwareprojekte und -probleme identifizieren und passende Strategien anwenden, um die Effizienz und Effektivität ihrer Arbeit zu steigern.

Agile, Lean Software Development, Scrum, XP und Kanban

Herkunft und Zusammenhang

  • Agile: Entstand 2001 aus dem Agile Manifesto und beschreibt eine Reihe von Prinzipien und Werten, die Flexibilität, Zusammenarbeit und Kundenorientierung in der Softwareentwicklung fördern. Agile umfasst verschiedene Methoden und Frameworks wie Scrum, XP und Kanban.

  • Lean Software Development: Basierend auf den Prinzipien der Lean Production aus der Automobilindustrie (insbesondere Toyota). Ziel ist es, Wert zu maximieren und Verschwendung zu minimieren. Wurde von Mary und Tom Poppendieck auf die Softwareentwicklung angewendet.

  • Scrum: Ein agiles Framework, das auf Iterationen, genannt Sprints, basiert. Es fördert Selbstorganisation, regelmäßige Inspektion und Anpassung.

  • Extreme Programming (XP): Eine agile Methode, die sich auf technische Exzellenz und kontinuierliche Verbesserung konzentriert. Enthält Praktiken wie Pair Programming, TDD und kontinuierliche Integration.

  • Kanban: Ursprünglich aus der Lean Production stammend, wurde Kanban von David J. Anderson auf die Softwareentwicklung übertragen. Es konzentriert sich auf die Visualisierung des Workflows und die Begrenzung von Work-in-Progress (WIP).

Kanban Principles, Practices und Values

Principles:

  1. Start with what you do now: Kanban baut auf bestehenden Prozessen auf.
  2. Agree to pursue incremental, evolutionary change: Veränderungen werden schrittweise eingeführt.
  3. Respect the current process, roles, responsibilities, and titles: Anerkennung der bestehenden Strukturen und Verantwortlichkeiten.
  4. Encourage acts of leadership at all levels: Jeder im Team wird ermutigt, Verantwortung zu übernehmen.

Practices:

  1. Visualize the Workflow: Darstellung der Arbeitsprozesse auf einem Kanban Board.
  2. Limit Work in Progress (WIP): Begrenzung der Anzahl gleichzeitiger Aufgaben, um Überlastung zu vermeiden.
  3. Manage Flow: Überwachung und Optimierung des Arbeitsflusses.
  4. Make Process Policies Explicit: Klare Definition und Kommunikation der Arbeitsregeln.
  5. Implement Feedback Loops: Regelmäßige Überprüfung und Anpassung der Prozesse.
  6. Improve Collaboratively, Evolve Experimentally (Kaizen): Kontinuierliche Verbesserung durch kleine, experimentelle Änderungen.

Values:

  • Transparency: Klare Sicht auf alle Arbeitsprozesse und Aufgaben.
  • Collaboration: Förderung der Zusammenarbeit im Team.
  • Customer Focus: Ausrichtung auf die Bedürfnisse des Kunden.
  • Flow: Kontinuierlicher und effizienter Arbeitsfluss.

Kanban Board und Cumulative Flow Diagram (CFD)

Kanban Board:

  • Funktion: Visualisierung des Workflows durch Spalten, die verschiedene Phasen des Arbeitsprozesses darstellen (z.B. To Do, In Progress, Done).
  • Elemente: Karten repräsentieren Aufgaben, die durch die Spalten bewegt werden, um den Fortschritt zu zeigen.

Cumulative Flow Diagram (CFD):

  • Funktion: Visualisierung der Arbeitselemente über die Zeit, um Engpässe und Arbeitsfluss zu analysieren.
  • Elemente: Zeigt die Menge der Arbeit in verschiedenen Phasen (Spalten des Kanban Boards) über die Zeit an.

Begriffe

Pull System:

  • Aufgaben werden basierend auf der Kapazität und Verfügbarkeit des Teams gezogen, nicht geschoben.

Limited WIP (Work in Progress):

  • Begrenzung der Anzahl gleichzeitiger Aufgaben, um Überlastung zu vermeiden und den Fokus zu verbessern.

Visualize the Workflow:

  • Darstellung des Arbeitsprozesses auf einem Kanban Board, um Transparenz und Effizienz zu erhöhen.

Cumulative Flow Diagram:

  • Visualisierung des Arbeitsflusses über die Zeit, zeigt die Menge der Arbeit in verschiedenen Phasen an.

Lead Time vs. Cycle Time:

  • Lead Time: Zeit von der Auftragserteilung bis zur Lieferung.
  • Cycle Time: Zeit, die benötigt wird, um eine Aufgabe vom Beginn der Bearbeitung bis zur Fertigstellung abzuschließen.

Kaizen:

  • Kontinuierliche Verbesserung durch kleine, inkrementelle Veränderungen.

Waste in Software Development:

  • Überflüssige Aktivitäten, die keinen Mehrwert liefern, wie unnötige Meetings, Wartezeiten, Defekte und redundante Arbeiten.

Principles of Lean

  1. Value: Kundenwert definieren und maximieren.
  2. Value Stream: Wertstrom analysieren und optimieren.
  3. Flow: Kontinuierlichen Arbeitsfluss sicherstellen.
  4. Pull: Aufgaben basierend auf Nachfrage ziehen.
  5. Perfection: Kontinuierliche Verbesserung anstreben.

Unterschiede und Gemeinsamkeiten von Scrum und Kanban

Gemeinsamkeiten:

  • Beide fördern Transparenz, Zusammenarbeit und kontinuierliche Verbesserung.
  • Beide nutzen visuelle Werkzeuge zur Verfolgung des Arbeitsfortschritts (Task Board in Scrum, Kanban Board in Kanban).
  • Beide zielen darauf ab, den Arbeitsfluss zu optimieren und Engpässe zu identifizieren.

Unterschiede:

  • Scrum: Strukturierte Iterationen (Sprints) mit festgelegten Rollen (Scrum Master, Product Owner, Entwicklungsteam) und Zeremonien (Sprint Planning, Daily Scrum, Sprint Review, Sprint Retrospective).
  • Kanban: Keine vorgeschriebenen Iterationen, Rollen oder Zeremonien. Fokus liegt auf kontinuierlichem Fluss und WIP-Limits.

Zusammenfassung

Agile, Lean Software Development, Scrum, XP und Kanban sind eng miteinander verbunden und haben gemeinsame Ziele: Flexibilität, Effizienz, Kundenorientierung und kontinuierliche Verbesserung. Kanban bietet spezifische Prinzipien und Praktiken, die den Arbeitsfluss visualisieren und optimieren, während Lean Prinzipien sich auf die Eliminierung von Verschwendung und die Maximierung des Kundenwerts konzentrieren. Scrum und Kanban bieten unterschiedliche Ansätze zur Organisation und Optimierung der Arbeitsprozesse, können jedoch auch kombiniert werden, um die jeweiligen Vorteile zu nutzen.

Tidy First

"Tidy First?" ist ein Buch von Kent Beck, das sich mit der Bedeutung von Code- und Datenstrukturen für die Softwareentwicklung beschäftigt. Hier sind die Hauptpunkte zusammengefasst:

  1. Ordnung vor Funktionalität: Beck argumentiert, dass es wichtig ist, den Code und die Datenstrukturen zuerst aufzuräumen, bevor man neue Funktionalitäten hinzufügt. Ein sauberer und gut strukturierter Code ist einfacher zu erweitern und zu warten.

  2. Kontinuierliches Refactoring: Regelmäßiges Refactoring ist entscheidend, um den Code lesbar und verständlich zu halten. Dies hilft dabei, technische Schulden zu vermeiden und die langfristige Gesundheit des Projekts zu sichern.

  3. Testgetriebene Entwicklung (TDD): Beck betont die Bedeutung von TDD, bei der Tests geschrieben werden, bevor der eigentliche Code implementiert wird. Dies stellt sicher, dass der Code korrekt ist und hilft dabei, Fehler frühzeitig zu erkennen.

  4. Lesbarkeit und Verständlichkeit: Der Code sollte so geschrieben sein, dass er leicht zu verstehen ist. Dies bedeutet, klare und aussagekräftige Namen zu verwenden, Kommentare sinnvoll einzusetzen und die Struktur des Codes logisch und konsistent zu halten.

  5. Kleine Schritte: Veränderungen sollten in kleinen, überschaubaren Schritten durchgeführt werden. Dies minimiert das Risiko von Fehlern und macht es einfacher, Probleme zu identifizieren und zu beheben.

  6. Verantwortung und Teamarbeit: Jeder im Team sollte Verantwortung für den Code übernehmen. Gemeinsame Standards und Praktiken helfen dabei, eine konsistente Codebasis zu gewährleisten.

  7. Werkzeuge und Techniken: Beck empfiehlt den Einsatz von Werkzeugen und Techniken, die das Aufräumen und Refactoring unterstützen, wie z.B. statische Codeanalyse-Tools und automatisierte Tests.

Zusammengefasst betont "Tidy First?" die Wichtigkeit einer sauberen und geordneten Codebasis als Grundlage für eine erfolgreiche Softwareentwicklung. Das Buch bietet praktische Ratschläge und Techniken, um dies zu erreichen und langfristig zu erhalten.

MPC

Acronyms

AcronymDescription
UEUnit of Execution

Patterns

Multiple patterns may be used concurrently.

How to find concurrency?

  1. Finding a general solution and applying that pattern
  2. Perform a dependency analysis
  3. Either progress to algorithm-structure or return to finding a general solution

Finding Concurrency

  • Task Decomposition pattern
  • Dependency analysis pattern

Algorithm structure

  • Task parallelism pattern

Supporting Structure

  • Fork / Join pattern

Implemenation mechanism

  • Processes & Threads

State Pattern

  • Allows an object to alter its behavior when its internal state changes

https://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/State_Design_Pattern_UML_Class_Diagram.svg/2880px-State_Design_Pattern_UML_Class_Diagram.svg.png

Task Decomposition Pattern

  • Decomposes a problem into concurrently executable tasks
  1. Define the solution space of a task, as independent as possible
  2. Define the features of said task

Possible solutions

  • Functional Decomposition: When splitting the program based on the function
  • Loop Splitting: Distinct iterations of a loop

Control Flow Graph

The control flow graph displays all possible execution paths of a program

alt text

-> Each buble represents a identifiable task

Dependency analysis pattern

Group Task Pattern

  • How to group tasks that make up a problem to simplify analysing dependencies

Order Task Pattern

  • How must the groups of tasks be ordered to satisfy constrains

Data Sharing Pattern

  • How is data shared amongst tasks

Solution

Shared Data can be:

  • read only
  • effectively-local
  • read write
    • accumulate
    • multiple-read single-write

Task Parallelism pattern

Dependencies

  • Order Dependencies must be encored
  • Data dependencies
    • Removable dependencies - can be removed by code / loop transformation
    • seperable dependencies - data strouctrues could be replicated (replicated data)

Schedule

  • How are tasks assigned to UEs and how are they scheduled for execution?

Static Scheulde

  • Defined a priori
  • low management effort but low numebers of tasks

Dynamic schedule

Higher management but for unknown or unpredictable task load

  • Define a task queue and when UE completes is current task, it gets a new one from the queue

-> UEs with tasks completed faster will take more from the queue

Work-Stealing: if UE has nothign to do, it looks to the queue of otehr UEs and takes a task to do (adds significant complexity)

Fork / Join Pattern

  • Implies the cloning of section of code into another (structurally - but not necessarily in terms of data) independent task
  • teh fork-join pattern is NOT an abstraction of the POSIX fork, if anything vice-versa

Use-cases

  • recursive structure, such as trees - lend themselves to fork-join
  • irregular sets of connected tasks
  • where diffeent functions are mapped onto different tasks
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

#define NUM_PROCESSES 4
#define ARRAY_SIZE 1000

int array[ARRAY_SIZE];

void sum_array(int start, int end, int* result) {
    for (int i = start; i < end; i++) {
        *result += array[i];
    }
}

int main() {
    // Initialize array with values 1 to ARRAY_SIZE
    for (int i = 0; i < ARRAY_SIZE; i++) {
        array[i] = i + 1;
    }

    pid_t pids[NUM_PROCESSES];
    int partial_sum[NUM_PROCESSES] = {0};

    for (int i = 0; i < NUM_PROCESSES; i++) {
        pids[i] = fork();

        if (pids[i] < 0) {
            perror("fork");
            exit(EXIT_FAILURE);
        } else if (pids[i] == 0) {
            int start = i * (ARRAY_SIZE / NUM_PROCESSES);
            int end = (i + 1) * (ARRAY_SIZE / NUM_PROCESSES);
            sum_array(start, end, &partial_sum[i]);
            exit(partial_sum[i]);
        }
    }

    int total_sum = 0;
    for (int i = 0; i < NUM_PROCESSES; i++) {
        int status;
        waitpid(pids[i], &status, 0);
        total_sum += WEXITSTATUS(status);
    }

    printf("Total sum using fork-join: %d\n", total_sum);

    return 0;
}

Geometric Decomposition

  • Sequence of operations on a core data structure

Solution

  • Assign chunks
  • Ensure data available for chunks
  • Define the updating process for chunks
  • Group and map chunks to UEs

Data sharing between chunsk

alt text

Exchange Options

  • (A) A-Priori - perform exchange
    • copying or messaging
    • updating is straight forward
  • (B) Computation and communication overlap
    • compute local data whils boundary data is being transmitted
    • updating requires multi-threading, communication and computation may work

Loop Parallelism Pattern

Loop parallelism is a technique to speed up programs by running loop iterations at the same time on multiple processors.

Problem

You have a loop in your code that takes a long time to run. How can you make it faster?

Solution

  1. Check Independence: Ensure each loop iteration can run without waiting for others.
  2. Parallelize: Use tools (like OpenMP) to split the loop so each part runs on a different processor.

Result: The loop runs faster because multiple processors handle different parts simultaneously.

Benefits

  • Speed: Reduces the time needed to run the loop.
  • Efficiency: Uses multiple processors effectively.

Considerations

  • Complexity: Managing parallel tasks and ensuring no conflicts (race conditions) can be tricky.
  • Debugging: Finding and fixing errors is harder in parallel programs.

Use it when: Your program has loops that take a long time and can run independently.

Solution

  • Check Independence: Ensure each loop iteration can run without waiting for others.
  • Parallelize: Use tools (like OpenMP) to split the loop so each part runs on a different processor.

Distributed Array Pattern

Distributed array is a method for dividing arrays between multiple processors to improve efficiency and performance in parallel computing.

Problem

Large arrays need to be processed efficiently across multiple processors. How can you distribute the array elements effectively?

Solution

  1. Partition Arrays: Divide the array into blocks that can be distributed among processors.
  2. Map Blocks to Processors: Assign blocks to processors in a way that balances the load and minimizes communication overhead.

Result: The array elements are processed more efficiently as each processor works on a different part of the array.

Benefits

  • Load Balancing: Distributes work evenly among processors.
  • Memory Efficiency: Ensures efficient use of memory by aligning array distribution with the flow of computation.

Considerations

  • Communication Overhead: Managing data exchange between processors can introduce delays.
  • Index Mapping: Aligning local and global indices requires careful management to ensure correct data processing.

Use it when: Your program involves large arrays that need to be processed in parallel, and you want to optimize performance and memory usage.

Single Programm Multiple Data Pattern (SPMD Pattern)

Single Program Multiple Data (SPMD) is a parallel computing model where multiple processors run the same program but on different data.

Problem

Managing interactions between multiple processors running the same program but on different data sets. How can this be organized efficiently?

Solution

  1. Initialize: Load the same program on all processors and establish communication.
  2. Unique Identifiers: Each processor gets a unique identifier to differentiate its tasks.
  3. Distribute Data: Each processor works on its own portion of the data.

Result: The program runs on multiple processors simultaneously, each handling different data, improving overall performance.

Benefits

  • Scalability: High scalability as each processor can handle different data independently.
  • Maintainability: Easier to maintain a single code base.

Considerations

  • Data Distribution: Efficient data distribution and management are crucial.
  • Synchronization: Proper synchronization to manage data dependencies and interactions between processors.

Use it when: You need to run the same operations on different data sets across multiple processors for better performance and scalability.

Data Decomposition

-> Organise by data

Flexibility

Granularity Know

  • Defines Chunks whose size and number are parameterizable -> Used for fitting the solution onto different HW • Impact of granularity

  • Overhead of dependency management of chunks versus computational effort - The time managing dependencies must be small wrt computational effort - Boundary cells

    • Cells at boundary of chunk -> set size increases with surface area
    • Cost = constant overhead + per cell effort
    • Computational effort -> scales with volume
    • Rough-tune dependency management with computational effort

Efficiency

  • Work versus overhead (see above)
  • How do chunks map onto UEs

Geometric Decomposition

General Solutions

  • Array based computations
  • Recursive data structures

alt text

Tasks

Distinct

  • independently of each other
  • do not rely on each other's results & resources during their execution

Dependent

  • relies on the results of another task

Processes

  • Unit of resource ownership
  • Unit of scheduling
  • POSIX defined

Definition

  • has its own main
  • owns resources (memory, file handles, ...)
  • is assumed to have exclusive access to all computing and hardware resources 100% of the time
  • may be managed by an operating system
  • in a multi-process OS, the OS will schedule the processes accoring to some policy
  • the memory protection unit (HW) will protecte the integrity of the process' memory from other processes

POSIX Processes

Portable Operating System Interface (POSIX)

  • Behavioural specification
  • defines how processes and threads are created and terminated
  • operating systems may (or may not) suppport POSIX

alt text

  • Process is created through a fork
  • fork clones (total copy) the calling process to child
  • child inherits copies of resources
    • child/parent variables are handled seperateely (copy on write)
  • parent and child run independently of each other
    • os scheduled seperately by the OS

POSIX Threads

  • Under POSIX, created within a process
  • Operates in the context of a process
  • Can access all process reosurce including memory
  • Kernel management threads -> kernel knows threads exist and schedules them
    • default-linux-behaviour
  • user-threads -> threading in user space
    • kernel does not know of their existence
    • user-threads library schedules, not OS scheduler, schedules threads
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define NUM_THREADS 4
#define ARRAY_SIZE 1000

int array[ARRAY_SIZE];
int partial_sum[NUM_THREADS] = {0};
pthread_t threads[NUM_THREADS];

void* sum_array(void* arg) {
    int thread_part = *((int*)arg);
    int start = thread_part * (ARRAY_SIZE / NUM_THREADS);
    int end = (thread_part + 1) * (ARRAY_SIZE / NUM_THREADS);

    for (int i = start; i < end; i++) {
        partial_sum[thread_part] += array[i];
    }

    pthread_exit(0);
}

int main() {
    // Initialize array with values 1 to ARRAY_SIZE
    for (int i = 0; i < ARRAY_SIZE; i++) {
        array[i] = i + 1;
    }

    int thread_parts[NUM_THREADS];
    for (int i = 0; i < NUM_THREADS; i++) {
        thread_parts[i] = i;
        pthread_create(&threads[i], NULL, sum_array, &thread_parts[i]);
    }

    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }

    int total_sum = 0;
    for (int i = 0; i < NUM_THREADS; i++) {
        total_sum += partial_sum[i];
    }

    printf("Total sum using pthreads: %d\n", total_sum);

    return 0;
}

UE - Units of Execution

  • Creation and destruction of threads are costly
  • reuse them as much as possible

OpenMP - Open Multi-Processing

  • Operates on top of an OS
  • Supports a shared memory model
  • Opens – per directive - a team of threads to parallelise code sections - Operates according to the Fork-Join pattern master thread

omp_get_thread_num()

  • retreive the unique integer of the currently executing thread within the parallel region
  • starting from 0

Run with as many threads as possible

#pragma omp parallel
{
    // Code to be executed in parallel
    printf("Hello, world! I'm thread %d\n", omp_get_thread_num());
}

Run multiple sections in parallel

#pragma omp parallel sections
{
    #pragma omp section
    {
        for (i = 0; i <= maxval; i++) {
            do_some_work_here
        }
    }

    #pragma omp section
    {
        for (i = 0; i <= maxval; i++) {
            do_some_other_work_here
        }
    }
}

Statememts

Run for loop

#include <stdio.h>
#include <omp.h>

int main() {
    #pragma omp parallel
    {
        #pragma omp for
        for (int i = 0; i < 10; i++) {
            printf("Thread %d is processing iteration %d\n", omp_get_thread_num(), i);
        }
    }
    return 0;
}

Lock

A lock is used, whenever a critical section must not be run with multiple threads in parallel

#include <stdio.h>
#include <omp.h>

int main() {
    // Parallel region
    #pragma omp parallel
    {
        // Each thread will execute this block
        // Only one thread at a time will be allowed to execute the critical section
        #pragma omp critical
        {
            // Critical section
            printf("Thread %d is inside the critical section\n", omp_get_thread_num());
        }
    }

    return 0;
}

Syncing threads

#include <stdio.h>
#include <omp.h>

int main() {
    // Parallel region
    #pragma omp parallel
    {
        // Some work done by each thread
        printf("Thread %d is doing some work before the barrier\n", omp_get_thread_num());

        // Synchronize all threads at this point
        #pragma omp barrier

        // After the barrier, all threads will execute this
        printf("Thread %d passed the barrier\n", omp_get_thread_num());
    }

    return 0;
}

Run on only master thread

#include <stdio.h>
#include <omp.h>

int main() {
    // Parallel region
    #pragma omp parallel
    {
        // This block of code is executed by all threads
        printf("This is executed by all threads\n");

        // The following block of code is executed only by the master thread
        #pragma omp master
        {
            printf("This is executed only by the master thread\n");
        }

        // This block of code is executed by all threads
        printf("This is executed by all threads again\n");
    }

    return 0;
}

Execute by one thread only

{
    #pragma omp parallel
    {
        #pragma omp single
        {
            // This block of code will be executed by only one thread
            printf("This is executed by only one thread\n");
        }

        // Code executed by all threads
    }
}

Task paralellism

#pragma omp parallel
{
    // Code executed by all threads
    
    #pragma omp task
    {
        // creates a task that can be executed asynchronously by any available thread in the team.
        // Code inside the task
    }
}

Do not Wait

  • nowait
a();
#pragma omp parallel
{
    b();
    #pragma omp for nowait
    for (int i = 0; i < 10; ++i) {
        c(i);
    }
    d();
}
z();

SIMD - Single Instruction, Multiple Data

Architectures

  • Single Instruction Single Data (SISD)
  • Single Instruction Multiple Data Streams (SIMD)
  • Multiple Instructions Single Data Stream (MISD)
  • Multiple Instructions Multiple Data Streams (MIMD)

Single Instruction Single Data (SISD)

  • Standard general-purpose single-core CPU operation
  • Generally associated with a single Functional Unit (ALU)

alt text

Single Instruction Multiple Data (SIMD)

  • A single instruction works on multiple data streams
  • Associated with multiple Functional Units (FU)
  • Abstracted to Single Instruction Multiple Threads (Nvidia)
  • Further abstracted to Single Program Multiple Data (pattern)

alt text

Single Program Multiple Data (SPMD)

Running the same programm on multiple UEs

(Multiply-accumulate) MAC Functional Units

  • Efficiency in Arithmetic Operations
  • Digital Signal Processing (DSP)
  • Machine Learning and AI
  • Scientific Computing

-> Many algorithms multiply and then accumulate

SIMD Vectorisation Operation

NEON Intrinsics

  • allows for efficient parallel processing of data, which can significantly enhance performance for certain types of computations.

https://developer.arm.com/architectures/instruction-sets/intrinsics/#q=

alt text

Single Instruction, Multiple Data (SIMD) instructions on ARM NEON (Advanced SIMD) are designed to perform the same operation on multiple data points simultaneously, enhancing performance in multimedia, signal processing, and other applications. Here is a summary of key aspects of SIMD instructions on ARM NEON:

Overview

  • SIMD Architecture: NEON operates on vectors, which are groups of elements (8, 16, 32, or 64 bits in size) processed in parallel.
  • Registers: NEON has 32 64-bit or 16 128-bit registers, referred to as Q (quad-word) and D (double-word) registers.

Data Types

  • Integer: 8-bit, 16-bit, 32-bit, and 64-bit integers.
  • Floating Point: 32-bit and 64-bit floating point.
  • Fixed Point: Supports fixed-point arithmetic, useful for certain DSP applications.
  • Polynomials: Supports polynomial arithmetic operations.

Key Instructions

  1. Load/Store Instructions:

    • VLDn: Load multiple elements from memory into NEON registers.
    • VSTn: Store multiple elements from NEON registers to memory.
  2. Arithmetic Instructions:

    • VADD: Vector add.
    • VSUB: Vector subtract.
    • VMUL: Vector multiply.
    • VMLS: Vector multiply and subtract.
    • VMLA: Vector multiply and accumulate.
  3. Comparison Instructions:

    • VCGE: Vector compare greater than or equal.
    • VCGT: Vector compare greater than.
    • VCLE: Vector compare less than or equal.
    • VCLT: Vector compare less than.
  4. Logical Instructions:

    • VAND: Vector bitwise AND.
    • VORR: Vector bitwise OR.
    • VEOR: Vector bitwise exclusive OR (XOR).
    • VBIC: Vector bitwise clear.
  5. Shift Instructions:

    • VSHR: Vector shift right.
    • VSHL: Vector shift left.
    • VRSHR: Vector round and shift right.
    • VRSHL: Vector round and shift left.
  6. Miscellaneous Instructions:

    • VABS: Vector absolute.
    • VNEG: Vector negate.
    • VSQRT: Vector square root.
    • VMOV: Vector move (transfer data between registers).
    • VDUP: Vector duplicate (replicate a scalar across a vector).

Execution

  • Parallelism: Executes operations on multiple data elements in parallel, improving throughput.
  • Pipeline: NEON instructions can be pipelined to enhance performance further.
  • Optimized Libraries: ARM provides optimized libraries (e.g., ARM Compute Library) leveraging NEON for common tasks.

Use Cases

  • Multimedia Processing: Efficient handling of audio, video, and image processing tasks.
  • Signal Processing: Enhancements in digital signal processing (DSP) applications.
  • Machine Learning: Accelerates inference operations for neural networks and other ML models.
  • Games and Graphics: Improves performance in graphics rendering and game physics calculations.

Performance Considerations

  • Alignment: Data alignment is crucial for maximizing performance.
  • Memory Access Patterns: Optimized memory access patterns can prevent stalls and improve efficiency.
  • Instruction Scheduling: Proper instruction scheduling can maximize NEON pipeline utilization.

ARM NEON provides a powerful set of SIMD instructions that significantly enhance the performance of applications requiring parallel data processing. Its comprehensive set of instructions covers a wide range of operations, making it a versatile tool in the ARM architecture.

Summary: Inter-Processor Communication (IPC) in the Document

Introduction to Inter-Processor Communication (IPC):

  • IPC is crucial for enabling communication and data exchange between multiple processors in a system. It is especially important in multicore and parallel processing environments where tasks are distributed across different processing units.

Key Concepts in IPC:

  1. Synchronization:

    • Synchronization ensures that multiple processes or threads coordinate their execution to maintain data consistency and avoid race conditions.
    • Techniques such as barriers, locks, and semaphores are commonly used to synchronize tasks.
  2. Data Sharing:

    • Efficient data sharing mechanisms are essential for performance in parallel systems.
    • Data can be shared using shared memory, message passing, or other IPC mechanisms depending on the system architecture and requirements.

IPC Mechanisms:

  1. Shared Memory:

    • Shared memory allows multiple processes to access the same memory space. It is fast and efficient for data exchange but requires careful synchronization to prevent conflicts.
    • Shared memory is often used in systems with a common physical memory accessible by all processors.
  2. Message Passing:

    • Message passing involves sending data from one process to another through messages. It is used in distributed systems where processes may not share a common memory.
    • Libraries such as MPI (Message Passing Interface) provide standardized functions for message passing.
  3. Semaphores:

    • Semaphores are synchronization primitives used to control access to shared resources by multiple processes.
    • They can be binary (indicating availability) or counting (indicating the number of available resources).
  4. Mutexes (Mutual Exclusion):

    • Mutexes are used to prevent concurrent access to a resource by more than one process or thread.
    • A mutex ensures that only one process can access a critical section of code at a time.
  5. Barriers:

    • Barriers synchronize multiple threads or processes, ensuring that they all reach a certain point in the execution before any of them can proceed.
    • Barriers are useful in parallel algorithms where stages of computation must be synchronized.

Implementation in Different Environments:

  1. POSIX (Portable Operating System Interface):

    • POSIX defines standard APIs for IPC mechanisms such as shared memory, semaphores, and message queues.
    • Examples include sem_open, sem_close, sem_post, sem_wait for semaphores, and shm_open, shm_unlink for shared memory.
  2. OpenAMP (Open Asymmetric Multi-Processing):

    • OpenAMP facilitates IPC in heterogeneous systems with different types of processors.
    • It uses mechanisms such as rpmsg (remote processor messaging) and virtio for efficient message passing between processors.
    • Example usage in OpenAMP involves creating endpoints and channels for message passing.
  3. Virtio:

    • Virtio provides a standardized interface for efficient data transfer between virtual machines and their host or between processors in a system.
    • It supports features like data queues and notifications to facilitate high-speed communication.

Examples of IPC Usage:

  1. Signal Handling in openAMP:

    • Signals are used to notify processes of events such as interrupts or task completions.
    • Example:
    void SignalHandler(int sig) {
        printf("signal handler called by signal %d\n", sig);
        flag--;
    }
    
    int main(void) {
        struct sigaction sig;
        sig.sa_handler = SignalHandler;
        sigemptyset(&sig.sa_mask);
        sigaction(SIGUSR1, &sig, NULL);
        while (flag > 0);
        printf("main terminates, flag = %d\n", flag);
    }
    
  2. Message Passing in MPI:

    • MPI provides functions such as MPI_Send and MPI_Recv to facilitate message passing in parallel applications.
    • It supports point-to-point communication and collective communication for coordinating multiple processes.

Performance Considerations:

  1. Latency and Bandwidth:

    • The performance of IPC mechanisms is influenced by latency (the time it takes to send a message) and bandwidth (the amount of data that can be transmitted in a given time).
    • Choosing the appropriate IPC mechanism based on the application's latency and bandwidth requirements is crucial for optimal performance.
  2. Overhead:

    • IPC mechanisms introduce overhead due to context switching, synchronization, and data transfer.
    • Minimizing overhead through efficient implementation and minimizing unnecessary communication can enhance performance.
  3. Scalability:

    • IPC mechanisms must scale efficiently with the number of processors to maintain performance in large systems.
    • Techniques such as hierarchical communication structures and load balancing can help achieve scalability.

Conclusion:

  • IPC is essential for enabling effective communication and coordination in multicore and parallel processing systems.
  • Understanding and utilizing various IPC mechanisms, such as shared memory, message passing, semaphores, mutexes, and barriers, are crucial for developing efficient parallel applications.
  • By optimizing IPC mechanisms for performance, developers can ensure that their applications run smoothly and efficiently on modern parallel and distributed systems.

Memory

Cache

  • fast memory close to, or integrated with, the processor
  • transparent to the programmer
  • When the code accesses a variable the cache controller checks whether the contents of the address is in cache memory
    • If yes -> cache hit
    • If no -> cache miss -> processor accesses the address in memory and reads a line/block
      • whilst being read by processor, the line read is also put into the cache memory

-> Good for speeding up transfers into the core

Paging

  • An address space is divided into pages (typically 4k, now -> 2M)

  • A physical memory is divided into frames of the same size

  • When code is required or a memory access happens the entire page is fetched into memory from the hard-disk

  • Due to relative addressing, the page can be placed in any frame in memory

  • Thus only the pages the code is actually using need to be in main memory

  • Efficient use of main memory

    • Support for multiprocessing

Summary: GPU Memory and Memory Management

Introduction to GPU Memory Architecture

  • GPU memory architecture is designed to maximize parallel processing efficiency. It includes multiple memory types with varying latencies and bandwidths to optimize performance for different tasks.

Key Memory Types in GPUs:

  1. Global Memory:

    • Global memory is the largest memory space on the GPU, accessible by all threads but with higher latency compared to other memory types.
    • It is used for storing large datasets and is essential for applications requiring significant data storage.
  2. Shared Memory:

    • Shared memory is a smaller, faster memory space accessible by all threads within a block.
    • It allows for efficient data sharing and communication between threads, significantly reducing the need for accessing slower global memory.
    • Shared memory is particularly useful for implementing data parallel algorithms, where multiple threads need to access the same data.
  3. Registers:

    • Registers are the fastest memory on the GPU, used to store variables for individual threads.
    • They offer low-latency access and are crucial for the execution of compute-intensive tasks.
    • However, the number of registers per thread is limited, and excessive use can lead to register spilling into slower memory.
  4. Constant Memory:

    • Constant memory is read-only memory that is cached and optimized for reading by multiple threads.
    • It is ideal for storing constant values that do not change during kernel execution, such as coefficients in mathematical computations.
  5. Texture Memory:

    • Texture memory is a specialized memory used for texture mapping in graphics rendering.
    • It is optimized for spatial locality and offers efficient data retrieval for 2D and 3D textures.

Memory Access Patterns:

  • Efficient memory access patterns are critical for achieving high performance on GPUs. Poor access patterns can lead to memory bottlenecks, reducing the overall throughput.
  • Coalesced Memory Access:
    • Threads in a warp (a group of 32 threads) should access consecutive memory addresses to achieve coalesced memory access.
    • Coalesced access minimizes the number of memory transactions, reducing latency and increasing bandwidth utilization.

Memory Hierarchies and Data Locality:

  • GPUs utilize a memory hierarchy to manage data locality and access speeds:
    • Registers > Shared Memory > Constant/Texture Memory > Global Memory.
  • Proper use of memory hierarchies can significantly enhance performance by ensuring that frequently accessed data resides in faster memory.

Memory Management Techniques:

  1. Memory Allocation:

    • Memory allocation on the GPU is managed through APIs such as CUDA and OpenCL.
    • Developers allocate memory using functions like cudaMalloc for CUDA or clCreateBuffer for OpenCL.
    • It is crucial to free allocated memory after use to prevent memory leaks using cudaFree or clReleaseMemObject.
  2. Data Transfer:

    • Data transfer between the host (CPU) and the device (GPU) is a critical aspect of GPU programming.
    • Transfers are managed using functions like cudaMemcpy or clEnqueueWriteBuffer.
    • Minimizing data transfer and maximizing data reuse on the GPU can reduce transfer overhead and improve performance.
  3. Shared Memory Utilization:

    • Shared memory can be explicitly managed within kernels to store intermediate results or frequently accessed data.
    • Synchronization primitives like __syncthreads() ensure that all threads in a block have a consistent view of shared memory.
  4. Memory Alignment:

    • Proper memory alignment is essential to avoid inefficient memory access patterns.
    • Aligning data structures to match memory boundaries can lead to more efficient memory transactions.

Optimization Strategies:

  1. Avoiding Bank Conflicts:

    • Shared memory is divided into banks, and accessing multiple addresses within the same bank can cause bank conflicts, leading to serialization.
    • To avoid bank conflicts, ensure that memory accesses are distributed evenly across different banks.
  2. Using Constant Memory:

    • Utilize constant memory for read-only data that is accessed frequently by multiple threads.
    • This reduces global memory access and leverages the caching mechanism of constant memory.
  3. Prefetching Data:

    • Prefetching involves loading data into shared memory before it is needed by threads, reducing the wait time for memory access.
    • This technique can be implemented using double-buffering strategies, where one buffer is being processed while the other is being loaded with new data.
  4. Leveraging Texture Memory:

    • For applications involving spatial data, such as image processing, use texture memory to take advantage of its spatial locality optimizations.

Conclusion:

  • Understanding and effectively utilizing the various types of GPU memory is crucial for maximizing the performance of parallel applications.
  • Proper memory management, efficient access patterns, and optimization strategies can significantly reduce memory bottlenecks and enhance computational throughput.
  • By leveraging the full potential of the GPU's memory hierarchy, developers can achieve substantial performance gains in their applications.

CPU Architectures

GP - CPU Architectures

alt text

Parallelizing CPU architectures

  • Multicore
  • Multiple FUs -> ALUs, Multipliers
    • Data parallelism
  • Multiple pipelines
    • Instruction parallelism
  • Multiple Heterogeneous PEs -> DSP, GPU ...

DSP Architectures (Digital Signal Processing)

  • Very application specific
  • Offers mac (.M)
  • Data access (.D)
  • GP-ALU (.L)
  • Shifter / ALU (.S)

alt text

Software Pipelining

  • Technique to reduce pipeline stalls from instruction
  • Together with VLIW get very tight loop kernels

alt text

Pipeline Architectures

CISC

  • Each construciton seperate
    • move mem, reg
    • move reg, reg
  • Microcode decodes instruction and performs operation

RISC

  • Store and load
    • lots of instructions required
    • hardware architecture, inc. pipelining makes execution fast
    • compiler friendly

Superpipelining

  • increase the number of instructions in the pipeline
  • instructions can be issued faster

alt text

Superscalar

  • Increase the number of pipelines
  • Several instructions issued in a cycle
  • scheduling becomes an issue

alt text

Scheduling

alt text

Co Processors

alt text

  • Processor recognises instructions as its own
  • Processor does not recognise instruction it passes it on to connected Co-Pros
    • Either recognised or not recognised
    • If not then unknown instruction exception
    • If then processed by Co-Pro
  • Co-pros usually have separate clocks
  • Often own channels to memory/cache (NEON)

-> Independent/parallel execution possible

ARM Cortex-A53 (v8 diagram)

alt text

NEON (v7 description)

  • Has instruction queue (16 deep) and data queue (8 entries)
  • A53 regards instruction as complete (performs checking and fetches)
  • NEON must decode and process instruction
  • If instruction/data queue full then A53 stalls

Co-processors face uncertain future

  • (Co-Pro) Instruction decoders expensive
  • (Co-Pro) Only really useful for generic operations
  • (Co-Pro) Video co-processors exis(ted) but standards advance so quickly
  • (CPU) Tight integration also costs G-CPU silicon
  • (Toolset) Added compiler maintenance because of architecture specific instruction sets

(SMT) Simultaneous Multi Threading

  • Intel calls this hyperthreading
  • Supporting cores execute two threads -> 4 cores -> 8 threads
  • Superscalar architecture necessary
  • Assumption is that CPU resources (ALU ...) are not always being used
    • With two threads in parallel better chance of available resources being used
  • BIOS and OS must support this feature
    • Core looks like two (thread capable) cores to OS
    • Need to lock process on core to avoid hyperthreading when it is turned on
  • Speedup for some applications, reported by Intel, ~30%
    • Improvement more likely dependent on externals like cache stalls than on FU availability
  • Security issues abound

(AMP) Asymmetric Multiprocessing

AMP - Scenarios

  • AMP with different Instruction Set Architecture (ISA)
    • Typically specialisation – PRU, GPU, DSP ... • AMP with same ISA
    • Task driven, one processor for comms, one for I/O ...
    • Master-slave (typical) or peer-to-peer driven
  • AMP with same ISA clocked at different rates
  • AMP with same ISA, different architectures (64/32-bit)
    • F.i. big.LITTLE
  • AMP with different or no OS

openAMP

  • rpmsg message passing service
  • Every device is a communication channel with a remote processor
    • Devices are called channels
  • Each has a source/destination address
// send
int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len);

// receive
struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev, rpmsg_rx_cb_t cb, void *priv, struct rpmsg_channel_info chinfo);

alt text

GPU Architectures

SM (Streaming Mutiprocessor)

  • Contains many Cores

NVIDIA Fermi Architecture

  • 32-Compute Unified Device Architecture cores

  • 4 SFUs

  • 16 L/S units

  • A GPU has threads (of execution)

alt text

Rendering Pipeline

  • 3D Models are calculated on the CPU in world coordinates
  • Vertices Shader converts these into a 2D model (local coordinates), applices textures
  • Geometry shader: optional additional manipulator
  • Fragment shader shades the individual pixels

alt text

Organization

alt text

Summary: GPU (Graphics Processing Unit) Concepts from the Document

Introduction to GPU Architecture

  • GPUs are specialized hardware designed for parallel processing. They excel at handling multiple operations simultaneously, making them ideal for tasks such as graphics rendering, scientific simulations, and machine learning.

Key Components and Functions

  1. Processing Elements:

    • GPUs consist of numerous smaller cores or processing elements that work together to perform computations.
    • These cores are designed for high-throughput computations, allowing the GPU to handle a large number of parallel tasks efficiently.
  2. Task Scheduling:

    • GPUs use a unique scheduling system to distribute tasks among the available cores.
    • Task scheduling in GPU architecture involves distributing tasks among multiple GPU cores based on workload and resource availability.
    • The scheduling is typically managed by the GPU driver and runtime environment, ensuring optimal use of the available cores.
  3. Command Queue:

    • A command queue in OpenCL (Open Computing Language) is a crucial component for managing the execution order of commands.
    • The command queue allows for asynchronous execution, enabling multiple tasks to be processed concurrently.
    • It also manages resource allocation and ensures that tasks are executed in the correct order.

Programming Models:

  1. OpenCL:

    • OpenCL is a framework for writing programs that execute across heterogeneous platforms, including CPUs, GPUs, and other processors.
    • It provides a standardized interface for developing parallel programs, allowing developers to harness the power of GPUs.
    • OpenCL programs consist of kernels, which are functions executed on the GPU, and the host code, which runs on the CPU and manages kernel execution.
  2. Single Instruction, Multiple Data (SIMD):

    • SIMD is a parallel computing architecture where a single instruction operates on multiple data points simultaneously.
    • GPUs leverage SIMD to process large datasets efficiently, as the same operation can be applied to many data points in parallel.
    • This approach is highly effective for tasks like image processing, where the same operation needs to be performed on each pixel.
  3. Single Program, Multiple Data (SPMD):

    • SPMD is a parallel programming model where multiple processors execute the same program but operate on different data.
    • In the context of GPUs, this means that each core runs the same kernel but processes different chunks of data.
    • This model simplifies the programming process and ensures scalability across a large number of cores.

Synchronization and Data Sharing:

  1. Synchronization:

    • Synchronization in GPU programming is essential to ensure that tasks are completed in the correct order and data integrity is maintained.
    • Techniques like barriers and atomic operations are used to synchronize threads within a kernel.
  2. Data Sharing:

    • Efficient data sharing mechanisms are crucial for performance in parallel processing.
    • GPUs provide shared memory spaces accessible by all threads within a block, enabling fast data exchange.
    • Developers must carefully manage data access patterns to minimize conflicts and maximize throughput.

Performance Considerations:

  1. Granularity:

    • The granularity of tasks—how finely they are divided—affects the performance of GPU programs.
    • Fine-grained tasks can exploit more parallelism but may incur higher overhead due to increased synchronization and data management.
  2. Load Balancing:

    • Ensuring even distribution of work across all GPU cores is essential for optimal performance.
    • Load balancing techniques help avoid scenarios where some cores are idle while others are overloaded.
  3. Memory Hierarchies:

    • Understanding the GPU memory hierarchy (registers, shared memory, global memory) is crucial for optimizing performance.
    • Efficient use of fast, low-latency memory (like registers and shared memory) can significantly boost performance.

Use Cases and Applications:

  • Graphics Rendering:

    • GPUs were initially designed for rendering graphics, providing the computational power needed for high-resolution, real-time rendering.
  • Scientific Computing:

    • GPUs are widely used in scientific computing for simulations, modeling, and data analysis due to their ability to handle large-scale parallel computations.
  • Machine Learning:

    • The parallel processing capabilities of GPUs make them ideal for training machine learning models, particularly deep neural networks.

Conclusion:

  • GPUs play a critical role in modern computing by providing the parallel processing power necessary for a wide range of applications.
  • Understanding the architecture, programming models, synchronization mechanisms, and performance considerations of GPUs is essential for leveraging their full potential in computational tasks.

Memory Categories

alt text

alt text

OpenCL

__kernel void add_numbers(__global float4* data, 
      __local float* local_result, __global float* group_result) {

   float sum;
   float4 input1, input2, sum_vector; // array of 4 floats which support vectorization
   uint global_addr, local_addr;

   global_addr = get_global_id(0) * 2;
   input1 = data[global_addr];
   input2 = data[global_addr+1];
   sum_vector = input1 + input2; // perform four floating-point additions simultaneously

   local_addr = get_local_id(0);
   local_result[local_addr] = sum_vector.s0 + sum_vector.s1 + 
                              sum_vector.s2 + sum_vector.s3; 
   barrier(CLK_LOCAL_MEM_FENCE);

   if(get_local_id(0) == 0) {
      sum = 0.0f;
      for(int i=0; i<get_local_size(0); i++) {
         sum += local_result[i];
      }
      group_result[get_group_id(0)] = sum;
   }
}

MPI

  • moving data around takes time and causes latency

MPI RUN

https://www.open-mpi.org/doc/current/man1/mpirun.1.php

run multiple programms

mpirun -np 2 a.out : -np 2 b.out
  • Runs in total 4 processes

  • Runs 2 processes for a.out with rank 0-1

  • Runs 2 processes for b.out with rank 2-3

Test Status non blocking

#include <mpi.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    MPI_Init(&argc, &argv);

    int rank, size;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    int data = rank;
    MPI_Request request;
    MPI_Status status;

    if (rank == 0) {
        // Initiate a non-blocking send
        MPI_Isend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &request);

        int flag = 0;
        while (!flag) {
            // Do some work here...
            printf("Rank 0 is doing some work while waiting for send to complete\n");

            // Check if the send is complete
            MPI_Test(&request, &flag, &status);
        }

        printf("Rank 0: Send completed\n");
    } else if (rank == 1) {
        // Initiate a blocking receive
        MPI_Recv(&data, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
        printf("Rank 1: Received data = %d\n", data);
    }

    MPI_Finalize();
    return 0;
}

Element-wise reduction

On the root process

#include <mpi.h>

int MPI_Reduce(const void *sendbuf, void *recvbuf, int count,
               MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm);

Distributed on many systems

#include <mpi.h>

int MPI_Allreduce(const void *sendbuf, void *recvbuf, int count,
                  MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);

Here are comprehensive answers to all 44 questions extracted from the document:

  1. Where have we seen reduction before?

    • Reduction is used in array operations where multiple elements are combined using an operation (e.g., sum, min, max) to produce a single result.
  2. What is the data scope of the variables in the following code?

    #include <omp.h>
    
    int i = 0; int n = 10; int a = 7; 
    
    #pragma omp parallel for 
    
    for (i = 0; i < n; i++) 
    { 
        int b = a + i; 
        ... 
    }
    
    • i and b are private to each thread, while n and a are shared among threads.
  3. What is the data scope of the variables in the following code?

    #include <omp.h>
    
    #pragma omp parallel for shared(n, a) 
    
    for (int i = 0; i < n; i++) 
    { 
        int b = a + i; 
        ... 
    }
    
    #pragma omp parallel for shared(n, a) private(b) 
    
    for (int i = 0; i < n; i++) 
    { 
        b = a + i; 
        ... 
    }
    
    • In the first loop, n and a are shared, i and b are private. In the second loop, n and a are shared, i is private, and b is explicitly private.
  4. What is the data scope of the variables in the following code?

    #include <omp.h>
    
    int a, b, c, n; 
    
    #pragma omp parallel for default(shared) private(a, b) 
    
    for (int i = 0; i < n; i++) 
    { 
        // a and b are ? variables 
        // c and n are ? variables 
    }
    
    • a and b are private, while c and n are shared.
  5. How do the machines boot in Asymmetric Multiprocessing (AMP) with different Instruction Set Architectures (ISA)?

    • Machines in AMP boot either after reset or on-demand. They may boot from a bootloader or reserved memory, or boot code may be provided on demand.
  6. How tightly coupled are the applications in AMP?

    • The level of synchronization, data transfer, and organization of communications determine the coupling of applications in AMP.
  7. How do you use signals in openAMP?

    • Signals in openAMP are used through handlers. For example:
    void SignalHandler(int sig) {
        (void) printf("signal handler called by signal %d\n", sig);
        flag--;
    }
    
    int main(void) {
        struct sigaction sig;
        sig.sa_handler = SignalHandler;
        (void) sigemptyset(&sig.sa_mask);
        (void) sigaction(SIGUSR1, &sig, NULL);
        while (flag > 0);
        (void) printf("main terminates, flag = %d\n", flag);
    }
    
  8. Which is preferable: thread put in wait queue or a busy loop?

    • Putting a thread in a wait queue is generally preferable as it avoids consuming CPU resources unnecessarily, unlike a busy loop.
  9. What determines which thread goes first once released?

    • The thread scheduling policy of the operating system determines which thread goes first once released.
  10. How many actions are distinct?

    • Actions are distinct based on their functionality and independence in the control flow graph.
  11. How many tasks are independent?

    • Tasks are independent if they do not rely on each other’s results and can be executed concurrently without dependencies.
  12. Is there potential for parallelisation?

    • Yes, if tasks are independent and can be executed concurrently, there is potential for parallelization.
  13. Discuss which pattern seems most relevant for grouping tasks, ordering tasks, and data sharing.

    • The patterns relevant for this are Task Decomposition, Dependency Analysis, and Data Sharing patterns.
  14. Identify the data categories: read-only, effectively-local, read-write, accumulate, multiple-read single-write.

    • Data categories help in understanding how data is accessed and modified by tasks:
      • Read-only: Data read by tasks without modification.
      • Effectively-local: Data modified by one task but not shared.
      • Read-write: Data read and written by tasks.
      • Accumulate: Data combined from multiple tasks.
      • Multiple-read single-write: Data read by multiple tasks but written by one.
  15. Is data decomposition feasible for the edge detection application?

    • Yes, as edge detection can be divided into smaller tasks that operate on different parts of the image independently.
  16. What are the tasks in data decomposition?

    • Tasks in data decomposition include breaking down the image into sections, processing each section for edge detection, and combining the results.
  17. Explain the granularity knob and the impact of granularity.

    • The granularity knob refers to adjusting the size of tasks. Fine granularity means smaller tasks with more parallelism, while coarse granularity means larger tasks with less parallelism but reduced overhead.
  18. Explain the terms presented in task decomposition and dependency analysis.

    • Task decomposition involves breaking down a problem into smaller tasks. Dependency analysis involves identifying dependencies among tasks to determine the execution order.
  19. What is a fork-join?

    • Fork-join is a parallel pattern where tasks are split (forked) into parallel subtasks and then combined (joined) after execution.
  20. Explain the forces and solutions in the fork-join pattern.

    • Forces include the need for parallelism and synchronization. Solutions involve using thread or process management to handle task execution and joining results.
  21. How do you allocate components in design space exploration?

    • Components are allocated based on performance, resource availability, and optimization goals.
  22. What are the stages of design space exploration?

    • Stages include requirement analysis, component selection, system modeling, simulation, and optimization.
  23. How do you schedule operations in design space exploration?

    • Operations are scheduled based on task dependencies, resource availability, and performance metrics.
  24. What is the importance of measuring performance in design space exploration?

    • Measuring performance helps in evaluating design choices, identifying bottlenecks, and optimizing the system.
  25. How do you handle unseen parallelisms and serializations in hardware?

    • By using techniques such as pipelining, out-of-order execution, and speculative execution.
  26. What are functional units in hardware abstraction?

    • Functional units are components in a processor that perform operations like arithmetic, logic, and memory access.
  27. What is the role of processing elements in hardware abstraction?

    • Processing elements execute tasks and operations in parallel computing architectures.
  28. What are tasks in software abstraction?

    • Tasks are units of work scheduled for execution by the operating system or runtime environment.
  29. What is the difference between binding and pinning in design space exploration?

    • Binding refers to associating tasks with resources, while pinning refers to fixing tasks to specific resources to avoid migration.
  30. What is the significance of hardware abstraction layers?

    • Hardware abstraction layers provide a consistent interface for software to interact with different hardware components.
  31. How do you map software functions to hardware components?

    • Mapping involves associating software functions with specific hardware units for execution, considering performance and resource constraints.
  32. What is the difference between a function and a procedure in software implementation?

    • A function returns a value and is used in expressions, while a procedure performs actions but does not return a value.
  33. How do you ensure data sharing over defined interfaces in task scheduling?

    • By using synchronization mechanisms, data structures, and communication protocols.
  34. What are the levels of synchronization required in asymmetric multiprocessing?

    • Synchronization levels include task-level, data-level, and system-level synchronization to ensure proper coordination and data integrity.
  35. How are communications organized in asymmetric multiprocessing?

    • Communications are organized using shared memory, message passing, and inter-process communication (IPC) mechanisms.
  36. What is the role of the platform management unit in Zynq UltraScale+ MPSoC?

    • The platform management unit manages system resources, power, and boot processes in Zynq UltraScale+ MPSoC.
  37. How does the PMU bootloader work in Zynq UltraScale+ MPSoC?

    • The PMU bootloader initializes the system, loads the boot image, and hands off control to the main application.
  38. What is the significance of the ELF file in rproc_boot?

    • The ELF (Executable and Linkable Format) file contains the executable code and data for remote processors in rproc_boot.
  39. How does the rproc_boot function handle resource tables?

    • The rproc_boot function parses resource tables to configure memory, peripherals, and communication channels for remote processors.
  40. What is the role of virtio in inter-processor communication?

    • Virtio provides a standardized interface for efficient inter-processor communication and data transfer.
  41. How does rpmsg handle message passing in openAMP?

    • Rpmsg (remote processor messaging) facilitates message passing between processors using a virtual communication channel.
  42. **What are the components of an rpmsg endpoint structure

?** - Components include the destination address, source address, payload buffer, and communication channel configuration.

  1. How does the scheduling of tasks occur in a GPU architecture?

    • Task scheduling in GPU architecture involves distributing tasks among multiple GPU cores based on workload and resource availability.
  2. What is the significance of a command queue in OpenCL?

    • A command queue in OpenCL manages the execution order of commands, enabling asynchronous execution and resource management.

These answers provide a comprehensive understanding of the questions related to multicore and parallel processing, as discussed in the document.

BSY

OS

  • control resource usage, granting resource requestst, accounting for usage, mediate conflicting requests.

alt text

CPU - Central Processing Unit

  • Fetch first instruction from memory into registers, decode it to determine its type and operands, execute it, and then fetch, decode and execute subsequent instructions.

Hardware Review

alt text

Hyperthreading / Parallel pipelines

  • Innerhalb eines Intel Cores, werden mehrere Threads gleichzeitig ausgeführt.
  • Optimierung der Aufgaben

alt text

Caches

Der Cache ermöglicht einen deutlich schnelleren Zugriff auf Daten als im Memory, Speicher. Der Cache ist aber relativ teuer in der Produktion.

  • a: quad-core chip with a shared L2 cache
  • b: quad core chip with seperate L2 caches

alt text

Memory

alt text

Input Output

  • Huge amount of I/O Devices, Hard Drives, Network Interfaces, Serial Board, Keyboard, Mouse, Graphics, Cameras, etc.
  • Devices connected with CPU via a Bus System (SW) I/O Interfaces (SW) and I/O Controllers (HW)
  • There are many different Bus Standars
    • PCI
    • PCIe
    • DMI
    • SATA
    • US

alt text

Operating System Variants

  • Mainframe Operating Systems
  • Server Operating Systems
  • Multiprocessor Operating Systems
  • Personal Computer Operating Systems
  • Handheld Computer Operating Systems
  • Embedded Operating Systems
  • Sensor Node Operating Systems
  • Real-Time Operating Systems
  • Smart Card Operating Systems

Context switch

Der Zustand eines Prozesses wird durch die Inhalte der Register auf dem CPU bestimmt.

  • Der Dispatcher befüllt die Register in der CPU.
  • Im Durchschnitt 6 Switches pro Sekunden

File Reading

  • 11 Schritte alt text

TRAP / Software interrupt

Der einzige Weg, um von User Space in den Kernel Space zu gelangen

Readme

BRICKED CT BOARD

  1. CONNECT 5V with PA9 (on opposite side of PA10)
  2. Connect BOOT0 with VDD (on opposite side of BOOT0)
  3. POWER ON BOARD
  4. PRESS RST BUTTON
  5. PRESS LOAD Button
  6. RELEASE RST BUTTON

Signale

  • Es gibt unterschiedliche Signaltypen. Die meisten sind jedoch auf eine "CLK - Clock" angewiesen.
  • Die meisten Signal sind auf eine Flanke angewiesen, dies ist besonders beim Bus Signal zu sehen.

NOT

In vielen Schemas wird ein negiertes Signal, dass heist wenn Signal=0, dann aktiv, mit einem "N"[Signalname] bezeichnet.

CLK - Clock

Die CLK ist ein (meistens) gleichbleibendes Signal.

https://www.cyberphysics.co.uk/graphics/diagrams/electronics/clockPulse.png

Synchronous Mode

  • Master und Slave haben ein geteiltes CLK Signal

Asynchroner Mode

  • Master und Slave haben je ein eigenes, voneinander unabhängiges CLK Signal

Synchrones Bus Signal

Der bus des CT-Boardes wird durch eine steigende Flanke (rising edge) des CLK-Signals getriggert.

Ein synchrones bus signal, besteht aus folgenden Komponenten:

  • Address lines A[31:0]
  • Data lines D[31:0]
  • Control
    • CLK
    • NE: NOT Enable, bestimmt wann das Signal aktiv ist ist
    • NWE: NOT Write Enable, bestimmt wann geschrieben wird
    • NOE: NOT Output Enable, bestimmt wann gelesen wird

alt text

Timing Diagram

alt text

NBL Signal - NOT Byte Line

Das NBL Signal zeigt, wann welche Bytes geschrieben werden sollen. Es ist invertiert, also 0=>aktiv

alt text

CPU Schreibzeit

alt text

GND - Ground

Das GND Signal wird als virtuellen Nullpunkt bezeichnet. Das GND Signal ist in einem System das meist verbreitete

VCC - Voltage Common Collector

Das VCC Signal ist die Eingangsspannung des Gerätes. Es wird immer relativ zum GND Signal bemessen.

GPIO (General Purpose Input/Output)

  • GPIOS sind allgemeine Anbindungen. Mit ihnen lassen sich digitale und analoge Signale lesen bzwh. schreiben.
  • Die Pins können mit Code / Software konfiguriert werden
  • Die meisten GPIO Pins haben eine alternative digitale oder analoge Funktion. Man kann also auswählen, was man braucht.

Nachfolgend eine Tabelle mit den Abkürzungen, diese sind weit verbreitet, und in den meisten Schemas (die Skizeen mit den Strichen und Blöcken) auch so bezeichnet.

Pin bezeichungBeschreibung
GPGeneral purpose
PPPush Pull
PUPull UP
PDPull Down
ODOpen Drain
AFAlternate Function

Funktionsmodis

Wie bereits oben beschrieben, haben GPIOs IMMER mehrere Funktionen. Diese können konfiguriert werden.

  • Mit den grauen Blöcken wird der Port jediglich konfiguriert.
  • Pinke Signale sind dann aktiv, wenn die alternativen Funktionen aktiv sind.

alt text

Konfiguration

Die Konfiguration von Pins wird in der echten Welt selten händisch gelöst. Dafür gibt es diverse IDE's die einem das Leben weitgehend vereinfachen. (z.B. STM32CubeIDE) Dies hat den Grund, da jede Konfiguration herstellerabhängig ist.

Die Registernamen haben ein R am Ende des Namens, was für Register steht. Bitte nenne es nicht MODER sonder MODE-REGISTER.

Direction (MODER)

Die Richtung muss immer konfiguriert werden. Gelesen kann vom GPIO auch dann, wenn dieser als Output definiert ist.

MODER[1:0]Beschreibung
00Input
01General purpose output mode
10Alternate function mode
11Analog mode

Output Type (OTYPER)

alt text

OTYPERBeschreibung
0Push - Pull (Reset State)
1Open Drain

Push Pull

Push Pull ist der Reset State. Bei diesem sind HIGH / LOW möglich. Der Pegel kann nach oben oder unten gezogen werden.

alt text

Ist Internal Signal

  • LOW, so ist das Output Signal HIGH (links)
  • HIGH, so ist das Output Signal LOW

Open Drain

Open Drain wird dann verwendet, wenn du mehrere Geräte an diesem Port anschliessen wilst. Der Pin kann NUR nach LOW gezogen werden.

Ist Internal Signal

  • LOW, so ist das Output Signal floating
  • HIGH, so ist das Output Signal LOW

Pull-up / Pull-down / floating

  • In der Elektronik haben wir das Problem, dass, wenn ein Signal nicht gesetzt wird, dann ist es schwebend (floating). Schwebend bedeutet dann, dass der Wert irgendetwas sein kann. Dies wollen wir soweit wie möglich vermeiden. Deshalb verbinden wir das Eingangssignal über einen Widerstand zu HIGH / LOW. Wird die Leitung nun von aussen gesetzt, so muss das Signal, entgegen des Widerstandes gezogen werden.
  • HIGH aktive Signale haben normalerweise eine Pulldown-Koniguration
  • LOW Aktive (invertierte / NOT) Signale normalerweise eine Pullup Konfiguration ()

alt text

Speed

OSPEEDR[1:0]BeschreibungGeschwindigkeit
00Low Speed (Reset State)2 MHz
01Medium Speed10 MHz
10High Speed50 MHz
11Very high speed100 MHz

## Code

Wie kann denn nun ein GPIO Pin konfiguriert werden. Wir können selten einen einzelnen GPIO über ein eigenes Register steuern. Wir müssen diesen über einen Port steuern. Denn ein Port ist der Zugang zum einzelnen GPIO Pin.

Ports liegen neben anderer Hardware direkt im Memory. Jeder Port hat eine Basis Adresse. Diese Adresse zeigt auf die wirklichen Register des Portes.

Der Port ist dann in unterschiedliche Sektionen aufgeteilt. Diese liegen direkt nebeneinander im Memory. Somit können wir ein Struct verwenden, um auf die Konfiguration zuzugreifen.

typedef struct {
    volatile uint32_t MODER;    /**< Port mode register. */
    volatile uint32_t OTYPER;   /**< Output type register. */
    volatile uint32_t OSPEEDR;  /**< Output speed register. */
    volatile uint32_t PUPDR;    /**< Port pull-up/pull-down register. */
    volatile uint32_t IDR;      /**< Input data register. */
    volatile uint32_t ODR;      /**< output data register. */
    volatile uint16_t BSRR[2];  /**< [0]: Bit set register, 
                                     [1]: Bit reset register. */
    volatile uint32_t LCKR;     /**< Port lock register. */
    volatile uint32_t AFR[2];   /**< [0]: Alternate Function register pin 0..7, 
                                     [1]: Alternate Function register pin 8..15. */
} reg_gpio_t;

// Hier sagen wir wo der GPIOA im Memory liegt
// Indem wir einen Pointer (Zeiger) erstellen, der direkt auf die Adresse zeigt
#define GPIOA   ((reg_gpio_t *) 0x40020000)
#define GPIOB   ((reg_gpio_t *) 0x40020400)
#define GPIOC   ((reg_gpio_t *) 0x40020800)
#define GPIOD   ((reg_gpio_t *) 0x40020c00)
#define GPIOE   ((reg_gpio_t *) 0x40021000)
#define GPIOF   ((reg_gpio_t *) 0x40021400)
#define GPIOG   ((reg_gpio_t *) 0x40021800)
#define GPIOH   ((reg_gpio_t *) 0x40021c00)
#define GPIOI   ((reg_gpio_t *) 0x40022000)
#define GPIOJ   ((reg_gpio_t *) 0x40022400)
#define GPIOK   ((reg_gpio_t *) 0x40022800)

Continously read from address

#define ADDRESS 0x43004000
volatile uint32_t* const registerAddress = (volatile uint32_t*) ADDRESS;
uint32_t value;

while(1) {
    value = *registerAddress;

    // Check if bit 6 is set
    if (value & (1 << 6)) {
        break;  // Exit the loop if bit 6 is 1
    }
}

Continously write to address

#define ADDRESS 0x43004000
volatile uint32_t* const registerAddress = (volatile uint32_t*) ADDRESS;
uint32_t value = 0x00;

while(1) {
    *registerAddress = value;
}

SPI - Serial Peripheral Interface

SPI wird für die serielle Kommunikation zwischen einem Microcontroller und Peripheriegeräten verwendet. Das System ist vom Typ Master-Multiple Slave.

alt text

SignalBeschreibung
SCLKSerial Clock
MOSIMaster Out Slave In, to all slaves
MISOMaster In Slave Out, all slaves to one master
SSXSlave Select X, Auswahl des Slaves

Der Master, auch main genannt, ist dabei für die abstimmung der Kommunikation der einzelnen Slaves zuständig.

Connection (wires)

  • Serial data line(s)
  • Optional control lines
Communication ModeBeschreibung
SimplexUnidirectional, one-way only
Half-duplexBidirectional, only one direction at a time
Full-DuplexBidirectional, both directions simultaneously
TimingBeschreibung
SynchronBeide Anbindungen nutzen ein geteiltes CLK Signal
AsynchronJede Anbindung nutzt ein eigenes CLK Signal

Implementierung

Die Implementierung wird dabei mit Hilfe einse Schieberegisters implementiert. Das Schieberegister (Buffer) wird gefüllt und dann ausgelesen. Der Grund ist, dass die Kommunikation unabhängig der aktuellen Aufgaben der CPU ausgeführt werden können.

https://upload.wikimedia.org/wikipedia/commons/thumb/c/c8/SPI_basic_operation%2C_single_Main_%26_Sub.svg/1920px-SPI_basic_operation%2C_single_Main_%26_Sub.svg.png

## Timing Diagramm

Das Timing Diagram sieht zwar Komplex aus, ist es aber eigentlich gar nicht.

https://upload.wikimedia.org/wikipedia/commons/thumb/f/f0/SPI_timing_diagram_CS.svg/1920px-SPI_timing_diagram_CS.svg.png

  1. Der Master zieht das CS (SS) Signal des gewünschten Slaves nach unten. Der Strich oben auf dem Namen bezeichnet das Signal als Low Aktiv.
  2. Nun werden die 8 Bit aus dem Schieberegister bidirektional gesendet. (Das MSB wird dabei normalerwise als erstes gesendet)
    1. Daten werden dabei auf dem toggling edge gesendet
    2. Während Daten auf dem samping edge gelesen werden. Dies wird dann gemacht, um Lesefehler aufgrund von Signalwechsel zu vermeiden. Es wird in der Mitte des Bites gelesen.
  3. Nachdem 8 Bit gelesen wurden, wird das CS Signal wieder angehoben. Somit ist die Kommunikation beendet.

SPI Modes (toggling / raising edge)

  • TX: toggling edge
  • RX: raising edge

alt text

## Synchronisierung

TX

Sobald das Signal (TXE / TX Buffer Empty) HIGH ist, kann das nächste TX Byte auf das Register SPI_DR geschrieben werden

### RXNE

Ist dann aktiv, wenn Daten auf SPI_DR sind. Also können wir dann Daten von diesem Register lesen gehen.

Modes

MODE=0, CPOL = 0, CPHA = 0, SCK idle state = low

MODE=1, CPOL = 0, CPHA = 1, SCK idle state = low

MODE=2, CPOL = 1, CPHA = 0, SCK idle state = high

MODE=3, CPOL = 1, CPHA = 1, SCK idle state = high

U(S)ART (Universal Asynchronous Receiver Transmitter)

  • UART wird für allgemeine serielle Kommunikation über eine serielle Schnittstelle verwendet. Dies wird häufig bei Microprozessoren verwendet.

  • Die Kommunikation ist asynchron, dafür muss jedes Paket einzel durch den Identifikator (START Bit) synchronisiert werden.

  • Im Gegensatz zu SPI hat UART nur zwei Leitungen.

  • Die Sendefrequenz muss vor dem Verbindungsaufbau bekannt sein. Sender und Empfänger haben eine eigene CLK, die aber die gleiche Frequenz hat.

  • Paritymode (even / odd) muss ebenfalls vor dem Verbindungsaufbau bekannt sein.

alt text

Protokoll

  • IDLE Zustand: HIGH
  • Bits werden auf der steigenden Flanke von CLK gelesen.

alt text

  1. Leitung nach LOW ziehen
  2. 5-8 Datenbits senden (LSB first)
  3. Parity senden
    • Even / Gerade Parity: (Wenn Anzahl 1=gerade, dann parity = 0)
    • Odd / Ungerade Parity: (Wenn Anzahl 1=ungerade, dann parity = 1)
    • None => keine
    • mark => HIGH
    • space => LOW

Eigenschaften

Transmission Rate (Baud Rate)

Gibt an, wie viele Symbole pro Sekunde gesendet / empfangen werden sollen

Typische Werte sind:

  • 2400
  • 4800
  • 9600
  • 19200
  • 38400
  • 57600
  • 115200

Anzahl an Datenbits

  • 5
  • 6
  • 7
  • 8

Anzahl an stop bits

  • 1
  • 1.5
  • 2

Parity

  • none
  • mark (logic HIGH)
  • space (logic LOW)
  • even
  • odd

CPOL=0,CPHA=0

CPOL=0,CPHA=1

CPOL=1,CPHA=0

CPOL=1,CPHA=1

RS-485

  • Braucht 4 Leitungen, jeh zwei zum Senden / Empfangen
  • Signale auf den 2 Leitungen sind gegenseitig gespiegelt, was weniger anfällig zu noice (Fremdspannungen) auf Leitungen ist.

alt text

I2C

  • Bidirektional 2 Wire
  • Mit CLK (SCL)
  • Daten werden auf SDA gesendet
  • Synchron, half duplex
  • Jedes Gerät auf dem Bus, hat eine einzigartige Adresse
  • Multi Master möglich

Protokoll

alt text

1. START

alt text

  1. SCL wird auf HIGH gezogen
  2. SDA wird nach unten gezogen
  3. 1 Bit=LOW wird auf SCL gesendet, damit wird der BUS in den BUSY Mode gesetzt.

2. Adresse + Richtung senden

alt text

  • 7 Bits für die Slave Adresse

  • Richtung

    • 0: Write (Master -> Slave, Slave=Empfänger)
    • 1: Read (Slave -> Master, Master=Empfänger)
  • Nicht verwirren lassen, die Kästen auf SDA sagen jediglich, dass da ein Bit ist.

3. ACK von Slave

  • Der Slave zieht die SDA Leitung zu LOW.
  • Macht dies der Slave nicht, so wird die Kommunikation beendet (STOP)

4. Daten von Transmitter

  1. Der Sender gibt 1 Byte auf den Bus aus
  2. Der Empfänger sendet ein ACK Signal auf SDA
    • Der Sender kann erneut Daten senden
    • Sendet der Empfänger kein ACK, so schreibt der Sender auch keine weitere Daten mehr auf den SDA

STOP

alt text

  1. Master zeiht SDA von LOW zu HIGH

# Timer / Counter

Ein Timer, auch Eieruhr genannt, hat den Zweck, zu sich wiederholenden Zeiten ein Signal oder einen Interrupt auszuführen.

Um den Einstieg ein wenig zu vereinfachen, kann ein Timer auch im Code nachgebaut werden.

while (1) {
	for (uint32_t arrs=0; arrs < ARRS; psc++) {
		for (uint32_t psc=0; psc < PSC; psc++) {
			// sleep for 1 CPU Cycle
			// increase psc by 1
		}
		// increase ARRS by 1
	}
	// ARRS OVERFLOW OCCURED
	// INTERRUPD CALLED
	UIF();
}

Zähler

BitsVonBis
16 bit02^16
32 bit02^32

Es gibt zwei Arten von Zählern:

### Up-Counter

  • Zählt nach oben, beginnt bei 0, endet bei ARR
  • Generiert einen Overflow, also wenn der Wert über ARR kommt

Down-Counter

  • Zählt nach unten, beginnt bei ARR bis 0
  • Generiert einen Underflow, also wenn der Wert unter 0 kommt.

2 Bit Zähler

alt text

alt text

Prescaler

Der Prescaler besimmt wie viele Impulse benötigt werden, damit der Counter erhöht / verringert wird.

Timer berechnen

NameSymbolEinheitBeschreibung
QuelleFCLKHzFrequenz des Eingangssignales
Prescaler WertPSC-
Auto Reload RegisterARR-Auf welchen Wert das Register gesetzt werden soll, wenn
Zeitintervaltoutsin welchem Abstand, der Timer ausgeführt werden soll

Aufgaben

  • Der ARR soll jede 100us erhöht werden
  • Der Counter soll im Interval von 1s ticken
  • FCLK = 1 Mhz

dann folgt folgendes daraus für den Prescaler (PSC)

  • FCLK = 1'000'000 Hz
  • (PSC+1) = 100us * FCLK
    • (PSC+1) = 0.000'100s * FCLK
    • (PSC+1) = 0.000'100s * 1'000'000Hz
    • (PSC+1) = 4200
    • PSC = 4200 - 1 = 4199

Und für den ARR

  • FCLK = 1'000'000 Hz
  • CLK = 100us
  • (ARR+1)= 1s / 100us
    • (ARR+1)=100'000
    • ARR = 100'000 - 1 = 99'999

alt text

# PWM - Pulse Width Modulation

Mit PWM wird mit gan schnellem ein-und ausschalten ein Sinussignal nachgemacht.

alt text

Duty cycle

Gibt in Prozent an, wie oft das Signal an ist.

  • 50% => 50%
  • 0.5 => 50%

Average Signal

ADC - Analog Digital Converter

Die Welt ist analog, dass heisst ein Wert kann von 0 - 1 alles mögliche sein, dass heisst auch 0.034566, oder 0.9999.

Um jetzt dieses analoge Signal auch in der digitalen Wert verwenden zu können, gibt es ADC's. Diese wandeln den analogen Wert in einen digitalen Wert um.

Wir machen uns dabei ein Phenomen der Elektrotechnik zugunsten, wo wir mit hilfe von Widerständen eine Spannung aufteilen können.

Wenn wir jetzt bei einem der Signale, 1-5V einen Komparator, auch Vergleicher genannt, hinzufügen, dann ist es relativ einfach zu verstehen wie der ADC funktioniert.

Das Verhältnis der Widerstände ist jeh nach ADC unterschiedlich.

alt text

Dem ADC wird ein sehr genaues Referenzsignal (VREF) hineingefügt.

Fehler

Full-scale-error = offset error + gain error

Quantization

  • Analoges Signal ist eine gerade
  • Digitals Signal kann nur gewisse Werte analoge Werte weiderspiegeln.
  • Fehler zwischen -0.5 und + 0.5, der kleinsten Auflösung (LSB)

alt text

Offset

  • Nullpunkt nicht korrekt eingestellt

alt text

Gain

alt text

Conversion Modes

Single ChannelMulti channel
Single ConversionConvert 1 channel, then stop.Convert all channels in group, one after the other, then stop. Sequence can be programmed
Continous conversionContinously convert 1 channel until stop order is givenContinously convert a group of several channels until the stop order is given. Sequence can be programmed

Timing

alt text

Register Bits

Common Control Register (ADC_CCR_ADC)

RegisterBeschreibungadditional
TSVREFEenable/disable temp sensor and vREFITNT
VBATEenable/disable vbatVBAT => Batteriespannung, nur wenn eine angeschlossen ist
ADCPRE[1:0]Prescaler for ADCCLKAPB2 clock divided by
00 -> 2
01 -> 4
10 -> 6
11 -> 8
, APB2 clock = 42 Mhz on this board

Status Register (ADC_SR)

RegisterBeschreibung
OVROverrun, Daten wurden überschhrieben
STRTConversion started (regular channel)
EOCEnd of conversion. Cleared by reading result of conversion

Control Register 1 (ADC_CR1)

RegisterBeschreibungAdditional
OVRIEOverride Interrupt enable
EOCIEEOC Interrupt enable
RES[1:0]Conversion resolution00 -> 12 bit
01 -> 10 bit
10 -> 8 bit
11 -> 6 bit
SCANEnable scan mode

ADC Control Register 2 (ADC_CR2)

RegisterBeschreibungAdditional
SWSSTARTStart conversion of regular channel by softwareIs cleared by hardware
EXTEN[1:0]External trigger for regular channel00 -> disabled
01 -> positive edge
10 -> negative edge
11 -> positive & negative edge
EXTSELExternal event select to trigger conversion of a regular group
ALIGNData Alignment0 -> right aligned
1->left aligned
EOCSEOC Selection0 -> EOC shows end of each sequence of conversions
1 -> end of each conversion
DMAEnable DMAkopiere direkt vom adc in den memory.
CONTContinous mode0 -> single conversion mode
1 -> continous conversion mode
ADONADC Onaktivert den ADC

Sample Time

ADC Sample time register

Konfiguriert die Zeit, in der die Spannung gemessen ist.

Sampling for channel: SMPx[2:0]

  • 0x00: '000b' -> 3 cycles
  • 0x01: '001b' -> 15 cycles
  • 0x02: '010b' -> 28 cycles
  • 0x03: '011b' -> 56 cycles
  • 0x04: '100b' -> 84 cycles
  • 0x05: '101b' -> 112 cycles
  • 0x06: '110b' -> 144 cycles
  • 0x07: '111b' -> 480 cycles

ADC_SMPR1

  • Address offset 0x0C
  • Reset Value: 0x0000 0000

alt text

ADC_SMPR2

  • Address offset 0x10
  • Reset Value: 0x0000 0000

alt text

Sequence of Channels (ADC_SQR1/2/3)

alt text

  • Address offset: 0x2C
  • Reset value: 0x0000 0000
RegisterBeschreibungAdditional
L[3:0]Sequence lengthL+1 = number of conversions in regular sequence
SQx[4:0] Channel number of xth conversion ins sequence1 <= x <= 16

e.g. SQ3[4:0] = 0x7 means the 3rd conversion takes place on channel 7

Rechnungen

Sampling Time

Max sampling rate

  • PBA2 -> ADC Frequency, by default 42Mhz => 42'000'000 Hz

Cache

  • Blocks sind in diesem Beispiel 4 Byte lang, können aber auch grösser, bzwh. kleiner sein.

Aufbau im Memory

  • Gruppen von Blocks werden im Memory hintereinander gebilted.
  • Die CPU kopiert gewisse Blocks mit deren ID in den Cache.
BezeichnungSymbol
Anzahl Bits für die Adressierungt + i + o
Anzahl Bits für Offseto
Anzahl Bits für Indexi
Anzahl Bits für Tagt
  • Grösse des RAM in Bytes (Memory Size): 2^(t+i+o)
  • Anzahl Zeilen im Cache: 2^i
  • Grösse einer Cache-Zeile in Bytes (nur Nutzdaten): 2^o
  • Grösse des Cache in Bytes (Cache Size, nur Nutzdaten): 2^i * 2^o

Cache Organization

alt text

alt text

Fully Associative

alt text

Direct Mapped

alt text

N-Way Set Associative

  • max index corresponds to number of sets (s=m/n)

alt text

Memory Hierarchy

alt text]

alt text

Principle of locality

for (i=0; i < 10000; i++) {
	a[i] = b[i];					// spatial locality
}

if (a[1234] == a[4320]) {			// temporal locality
	a[1234] = 0;
}

Interrupt

Periodic Query of Status Information (Polling)

  • Synchronous with main programm
  • Advantages
    • Simple and straightforward
    • Implicit synchronization
    • Deterministic
    • No additional interrupt logic required
  • Disadvantages
    • Busy wait (wastes CPU time)
    • Reduced trhoughput
    • Long reaction times

alt text

if (spi_is_txe_set()) {
	spi_write_data(...);
}

if (uar_is_rxne_set()) {
	uard_data = uart_read_data();
}

if (adc_is_eoc()) {
	adc+data = adc_read_data();
}

Interrupt Driven I/O

alt text

Interrupt Performance

alt text

  • tISR > "Time between two interrupt events"
    • Some interrupt events will not be serviced (lost)
      • Data will be lost
    • fINT as well as tISR may vary over time
      • Average may be ok, but individual interrupt events may still be lost

Impact on system performance

Keyboard

Unit Converter

tISR = 6 us = 0,000006s fInt=20Hz = 20 1/s

Impact = 20 Hz * 6us * 100% = 0.012%

Interrupt Priorities

alt text

Code


void TIM2_IRQHandler(void)
{
}

/**
 * \brief  Timer 3 ISR: Generating load
 */
void TIM3_IRQHandler(void)
{
    hal_timer_irq_clear(TIM3, HAL_TIMER_IRQ_UE);
    tim3_interrupt_counter++;
}

CT1 - HS23

Cortex Architektur

Die Cortex Architektur ist auch als ARM Architektur bekannt. Diese ist in den meisten Embedded Prozessoren, in letzter Zeit aber auch in Mobile, sowie Desktop (Apple Silicon) vertreten.

CPU Modell

Die CPU besteht aus einzelnen Komponenten. Diese sind in folgende:

  • Core Registers
  • 32 Bit ALU (Arithmetic Logic Unit)
  • Flags (APSR)
  • Control Unit with IR (Instruction Registers)
  • Bus Interface

16 Core Registers

  • Jedes Register ist 32 Bit breit
  • 13 General Purpose Register
    • Low Registers R0 - R7
    • High Registers R8 - R12
    • Used for temporary storage of data and addresses
  • Program Counter (R15)
    • Addresss der nächsten Instruktion
  • Stack Pointer (R13)
    • Last-In First-Out für temporäre Datenspeicherung
  • Link Register (R14)
    • Enthält die Adresse zu der zurückgesprungen wird, wenn eine Routine fertig ist.

In den meisten Fällen werden nur die unteren Low Registers (Byte verwendet). Würde die Standardeinheit jetzt 16 oder gar 32 Bit sein, dann müssen die High Registers zusätzlich noch gelesen werden.

ALU (Arithmetic Logic Unit)

  • 32 bit wide data processing unit
    • Inputs A and B
    • Results in C
  • Integer Arithmetic
    • Addition / Subtraction
    • Multiplication / Division
    • Sign extension
  • logic operations
    • AND
    • NOT
    • OR
    • XOR
  • shift /rotate
    • left
    • right

Flags (APSR)

Bits werden von der CPU anhand des Resultats der ALU gesetzt

WertBedeutung
NNegative
 ZZero
CCarry
VOverflow

Control Unit

  • Instruction Register (IR)
    • Machine code (opcode) of instruction that is currently being executed
  • Controls execution flow based on instruction on IR
  • Generates control signals for all other CPU components

Bus Interface

  • Interface zwischen interner CPU und externen Bus Systemen
  • Beinhlatet register um addressen zu speichern

Instruction Set Architecture (ISA)

Instruction Set

  • Verfügbare Anweisungen

Processing Width

  • Bitbreite (8/16/32 Bit)

Register Set

  • Wie viele Register gibt es?
  • Wie gross sind die Register?

Addressodell

  • Wie kann auf Speicher zugegriffen werden?
  • Wie kann auf IO zugegriffen werden?

### Arbeitsweise

Prozessoren interpretieren binären Code als Instruktionen.

Styleguide

7 Segment Anzeige

Eine 7-Segment-Anzeige wird für Hexadezimalzeichen verwendet. Dabei wird jedes Segment der Anzeige einzeln angesteuert.

Theorie

Bei der Zahl 1 würden die Segment B und C aufleuchten. Im Register heisst dass dann die Bits 1 und 2 aktiv sind. Somit würde der Wert 0000'0110 bzwh. 0x06 sein.

Ist die Anzeige active-low (invertiert), dann muss 1111'1001 bzwh. 0xF9 geschrieben werden.

Bei 16 Zeichen ist die manuelle Umwandlung relative mühsam. Wir können das Umwandeln aber auch einfach dem Compiler überlassen. Dies hat zudem den Vorteil, dass es deutlich einfacher zu verstehen ist.

Code

Ich empfehle den Code immer so "blöd" wie möglich zu machen. Jedoch ist es auch wichtig, dass nicht einfach zufällige Zahlen irgendwo stehen (sog. Magic Numbers).

Define für jedes Segmentstück definieren

Wie bereits oben beschreiben, ist es relativ mühsam die einzelnen Zahlen zu definieren. Um dies zu vereinfachen können wir die einzelnen Bits einem define zuordnen.

Dass könnte dann z.B. so aussehen. Es ist auch möglich die decimalzahlen, bzwh. die HEX Zahlen direkt zu verwenden.

#define LED_A       1<<0
#define LED_B       1<<1
#define LED_C       1<<2
#define LED_D       1<<3
#define LED_E       1<<4
#define LED_F       1<<5         
#define LED_G       1<<6
#define LED_DP      1<<7

Definieren der Zahlen

Nun ist es wessentlich einfacher diversen Zeichen darzustellen. Dazu müssen wir nur noch die verwendeten Segmente mit einem ODER verknüpfen.

#define LED_SEGMENTE_ZAHL_1 (LED_B | LED_C)
#define LED_SEGMENTE_ZAHL_2 ...

Umwandeln

Das umwandeln ist durch einen array mit den 16 Zeichen oder ein switch case möglich. Ich empfehle den Ansatz des arrays, da dieser weniger Aufwand ist.

const static uint8_t segment_zeichen[16] = {
    LED_SEGMENT_ZAHL_1,
    LED_SEGMENT_ZAHL_2,
    ...
};

Entrpellung

Im Gegensatz zu digitalen Schaltern, sind analoge Schalter nicht sofort 1/0. Sie prellen.

Um dieses Problem zu lösen, werden Flanken erkannt. Also die Zuständsänderungen.

Flankenerkennung

uint8_t is_positive_flank(uint8_t previous, uint8_t current, uint8_t mask)
{

    if ((previous & mask) == 0x00 && (current & mask) == mask)
    {
        return 1;
    }

    return 0;
}

uint8_t is_negative_flank(uint8_t previous, uint8_t current, uint8_t mask)
{

    if ((previous & mask) == mask && (current & mask) == 0x00)
    {
        return 1;
    }

    return 0;
}

Verwendung

#define ADDR_SWITCHES ...

#define MASK_OF_INTEREST 1<<2

int main ()
{
    uint8_t previous_value = read_byte(ADDR_SWITCHES);
    uint8_t current_value = read_byte(ADDR_SWITCHES);

    uint8_t counter = 0;
    while (1)
    {
        current_value = read_byte(ADDR_SWITCHES);

        if (is_positive_flank(previous_value, current_value, MASK_OF_INTEREST))
        {
            // do something
            counter += 1;
        }

        write_byte(LEDS, counter);

        previous_value = curent_value;
    }
}

Assembler

Special Characters

Charakter(s)Meaning / Usage
#Precedes a number (decimal 1234 or hex 0x1234)
[ ]Content in squared brackets is interpreted as address
=Pseudo- Instruction: Assembler reserves space in literal pool to store the value following the "=" sign. The value located at that address is then loaded to the target address.
{ }Enumeration, e.g. comma seperated list of registers to be pushed on stack / written to memory
!"increment after" in load/store multiple
,Seperates operands
;Preceds a comment
Other Characters at beginning of line (no leadnig blanks) are interpreted as label

Flags

Flag Meaning ActionOperands
Negative MSB = 1 N = 1 signed
Zero Result = 0 Z = 1signed, unsigned
 Carry CarryC = 1unsigned
 OverflowOverflow V = 1 signed

N: Wenn MSB des Resultats =1 ist

Z: Wenn Resultat =0 ist

C: Wee

Overflow

The overflow flag is thus set when the most significant bit (here considered the sign bit) is changed by adding two numbers with the same sign (or subtracting two numbers with opposite signs). Overflow cannot occur when the sign of two addition operands are different (or the sign of two subtraction operands are the same).

Usage

MRS <Rd>, APSR          ; Rd = APSR
MSR APSR, <Rn>          ; APSR = Rn

Calculation

Binäre Subtraktion

HEX:    0x82 - 0x12
BIN:    1000'0010 - 0001'0010

subtrahend invertieren
        1000'0010 - 1110'1101
    
subtrahend +1 
        1000'0010 - 1110'1110
    
Zahlen addieren
        1000'0010 +
        1110'1110
        ---------
       10111'0000
        ========= 




Data Transfer

Transfer Types

Alt text

Alt text

Moving Data

Copy value of Rm to Rd

Low and High registers

MOV <Rd>, <Rm> 

Only low registers

MOVS <Rd>, <Rm> 

Exampes

address		opcode	instruction 	comment
00000002 	4621 	MOV R1,R4 		; low reg to low reg

Loading Literals

Immediate Data

  • Copy immediate 8-bit value (literal) to register (only low registers)
  • 8 Bit literal is part of the opcode (imm8)
  • Registerbits 31 to 8 set to 0
MOVS <Rd>, #<imm8>

Assembler Directive

  • Symbolic definitions of literals and constants
  • Comparable to #define in C
MY_CONST8 EQU 0x12
MOVS R1, #MY_CONSTS

Load - LDR (literal)

  • Indirect access relative to PC
  • PC offset
  • If PC not word-aligned, align on next upper word-address
LDR <Rt>. [PC, #<imm>]

Pseudo Instruction

			LDR R3,myLit


myList		DCD	0x12345678

Storing Data

Arithmetic Operations

 MnemonicInsructionFunction
ADD /ADDSAdditionA + B
ADCSAddiction with carry A + B + c
ADR Address to register PC + A
SUB / SUBS Subtraction A - B
SBCSSubtraction with carry (borrow)A - B - NOT(c)
RSBS Reverse Subtract (negative)-1 * A
MULS MultiplicationA * B

Add Instruction

Update flags

  • Update of Flags
  • Result and two operands
  • Only low registers
ADDS <Rd>, <Rn>, <Rm>       ; Rd = Rn + Rm

Dont update flags

  • No update of flags
  • High and low registers
  • Rdn contains result and operand
ADD <Rdn>, <Rm>             ; Rdn = Rdn + Rm

Immediate (3 bytes)

  • Update of flags
  • Two different low registers and immediate value 0-7
ADDS <Rd>,<Rn>, #<imm3>     ; Rd = Rn + <imm3>

Intermediate (8 bytes)

  • Update of flags
  • Low register with intermediate value 0-255d
  • Rdn stores result and operation
ADDS <Rdn>, <#imm8>         ; Rdn = Rdn + <imm8>

Negative Zahlen

2er Komplement

RSBS <Rd>, <Rn>, #0
Rd = 0 - Rn

Subtraction

SUBS <Rd>, <Rn>, <Rm>       ; Rd = Rn - Rm
                            ;    = Rn + NOT(Rm) + 1

Mutli-Word Arithmetic

ADCS <Rdn>, <Rm>            ; Rdn = Rdn + Rm + C
SBCS <Rdb>, <Rm>            ; Rdn = Rdn - Rm - NOT(C)
                            ;     = Rdn + NOT(Rm) + C

Multiplication

MULS <Rdm>, <Rn>, <Rdm>     ; Rdm = Rn + Rdm

Assembler

Load value from registers

### Load Byte value

ADDR_DIP_SWITCH_7_0         EQU     0x60000200

LDR     R0, =ADDR_DIP_SWITCH_7_0 	; read dip switch address into r0
LDRB    R0, [R0]					; read dip switches 15 to 8 into r0

### Load half word value

ADDR_DIP_SWITCH_7_0         EQU     0x60000200

LDR     R0, =ADDR_DIP_SWITCH_7_0 	; read dip switch address into r0
LDRH    R0, [R0]					; read dip switches 15 to 8 into r0

Storing Value

Store byte

ADDR_LED_31_24              EQU     0x60000103

LDR     R6, =ADDR_LED_31_24 		; store led address in R6
STRB    R3, [R6] 					; display array_index

Store halfword

ADDR_SEG7_BIN   			EQU		0x60000114

LDR     R6, =ADDR_SEG7_BIN			; load address
STRH    R0, [r6, #0]				; Store Index and Value to SSD

Bitwise AND

Bitwise and between value and address

BITMASK_LOWER_NIBBLE        EQU     0x0F

LDR		R2, =BITMASK_LOWER_NIBBLE	; read address of lower bitmask nibble
ANDS	R0, R0, R2					; store into r0 <- (r0 and mask: r2)

Incrementing address

Increment R4 by one

ADDS	R4, R4, #1					; increment register address

Shift

Shift Left

LSLS	R1, R1, #1					; increment index by multiplying it x2

Moving Register

MOV		R5, R1						; copy R1 into R5

Type Casting

Sign Extension (signed)

Extend word without changing value

SXTB    R3, R7                     ; extend R7 into R3 (8 bit to 32 bit)
SXTH    R3, R7                     ; extend R7 into R3 (16 bit to 32 bit)

Zero Extension (signed)

Extend word without changing value

UXTB    R3, R7                     ; extend R7 into R3 (8 bit to 32 bit)
UXTH    R3, R7                     ; extend R7 into R3 (16 bit to 32 bit)

Bitmanipulation

OR

ORRS    R3, R7                     ; OR MASK

CLR - Clear Bits

BICS    R3, R7                     ; 

XOR - Bitwise invert

EORS    R3, R7                     ; 

AND

ANDS    R3, R7                     ; 

Bits löschen

ADDRESS      ECU     ...

LDR         R6, =ADDRESS        ; ADDRESSE in R6 laden
LDR         R1, [R6]            ; Wert von R6 in R1 laden
LDR         R2, =0x2220         ; Maske 0x2220 in R2 laden
BICS        R1, R2              ; Bits R2 in R1 löschen
STR         R1, [R6]            ; R1 in R6 schreiben

Kontrollstrukturen

Switch Case


NR_CASES                EQU     0x2

jump_table      ; ordered table containing the labels of all cases
                ; STUDENTS: To be programmed 
				DCD		case_dark
				DCD 	case_add
				DCD		...


case_dark       
                LDR  R0, =0
                B    display_result  

case_add        
                ADDS R0, R0, R1
                B    display_result


                CMP	R2, #NR_CASES
				BHS	case_bright
				LSLS R2, #2		; * 4
				LDR R7,=jump_table
				LDR R7, [R7, R2]
				BX R7

Alu

def has_carry(result, bit_limits):
    m = (1<

Memory

Directives

REQUIRE8

specifies that the current file requires 8-byte alignment of the stack

### PRESERVE8 specifies that the current file preserves 8-byte alignment of the stack

THUMB

  • Provides 16 Bit instruction set
  • fetches instructions by 2 bytes

### ARM

  • Provides 32 bit instruction set
  • Fetches instructions by 4 bytes

DCB (1 byte)

Allocates one or more bytes of memory, and defines the initial runtime contents of the memory

DCD (4 bytes)

Allocates one or more words of memory, aligned on 4-byte boundaries, and defines the initial runtime contents of the memory.

DCW (2 bytes)

Allocates one or more halfwords of memory, aligned on 2-byte boundaries, and defines the initial runtime contents of the memory

### SPACE (1 byte)

store_table             SPACE   16 ; reserves 16 byte

AREA Instruction

The AREA directive instructs the assembler to assemble a new code or data area. Areas are independent, named, indivisible chunks of code or data that are manipulated by the linker.

    AREA name{,attr}{,attr}...

Arm Reference

Code

    AREA myCode, CODE, READONLY

Variables

    AREA myAsmVar, DATA, READWRITE

    ; VARIABLES

    ALIGN

Impressum

Verlass dich und du wirst verlasssen.

Obligationen

"obligo" bedeutet verpflichten im latein.

Kommen bei folgenden Aktionen zustande

  • Verträge
  • Schadenersatz (OR 41)
  • Ungerechtfertigte Bereicherung(OR 62)
  • Steuergesetz

Sitte

Für bestimmte Lebensbereiche einer Gemeinschaft geltende, dort übliche, als verbindlich betrachtete Gewohnheit, Gepflogenheit, die im Laufe der Zeit entwickelt, überliefert wurde.

Behauptung

Wird druch Grundlage (Gesetzesartikel) und notwendigen Beweis gestützt.

Schlussfolgerung

Gestützt auf Grundlage (Gesetzesartikel) und notwendigem Beweis ergibt sich die Schlussfolgerung.

Gewaltentrennung

Drei unabhängige Institutionen auf eidgenössischer, kantonaler und kommunaler Ebene.

  • Legislative: Parlamente & Bürger
  • Exekutive: Verwaltung
  • Judikative: Gerichte

Rechtsordnung

  • Rang (Verfassung, Gesetz, Verordnung)
  • erlassendem Gemeinwesen (Bundesrecht, kantonales- und Gemeinderecht)
  • Rechtsquelle (geschriebenes Recht, Gewohnheitsrecht, Gerichtspraxis, ZGB 1)
  • Beteiligten Personen (Privatrecht, öffentliches Recht)

Rechtsart

Privatrecht

Private - Private

Öffentliches Recht

öffentlich - Private

  • Notengebung an der ZHAW

Betreibung

Bricht eine Partei einen Vertrag, dann kann der andere Partner die Vertragbrechende Person betreiben.

Parteikosten wegschlagen

  • Die Kosten der eigenen Verteidigung müssen selber übernommen haben.

Rechtsstaat

Legislative: Parlamente & Bürger Exekutive: Verwaltung Judikative: Gerichte

Weshalb gibt es überhaupt Recht?

Gibt es keine Regeln, so muss jeder mit dem anderen aushandeln. Es ist gesellschachtflich einfacher sich an Regeln zu halten.

Strafverfahren

Essentials

  • Verfahren sind in StGB & StPO geregelt. Polizei ungerliegt überweigend kantolaner Obheit & ist dort geregelt.
  • Ortliche Zuständigkeit ergibt sich aus dem Tag- oder ERfolgsort.
  • Für Antragsdelikte gilt eine Frist von 3 Monaten
  • Staatsanwalt (StA) leitet Untersuchung, muss belastende & entlastende Aspekte sammeln
  • Als "Opfer" hat man nur begrenzte Einsicht in Untersuchungsmassnahmen. Ausser man bringt sich als Privatstrafkläger ein!
  • StA stellt wentweder Verfahren ein, straft (max. 6 Monate Freiheitsstrafen und/oder 180 Tagessätze) oder überweist den Fall zur Beurteilung an das Strafgericht.

Vorgehen

  1. Anschreiben
  2. Straffanzeige einreichen

## Aufgaben

Staatsanwaltschaft

  • Belastende Aspekte sammeln
  • Entlastende Aspekte sammeln

Zivilprozess

Essentials

  • Vor dem örtlich / sachlich zuständigen Gericht
  • In der REgel vorher eine Schlichtungsverhandlung / Friedensrichter
  • In Zivilverfahren muss regelmässig ein Gerichtskostenvorschuss, der vom Streitwert abhängt, bezahlt werden! Ohne Zahlung kein Prozess!
  • In Zivilrechtsverfahren muss der Kläger dem behaupteten Anspruch beweisen - Das Gericht sucht keine Beweise
  • Wer den Zivilprozess verliert, muss die Gerichtskosten sowie Parteikosten der anderen seite übernehmen.
  • Wer einene Förderungsprozess gewinnt, hat das Geld noch nicht...

Gutes Geld nicht schlechtem Geld hinterherwerfen

Andere Möglichkeiten

  • Richter einigen sich gemeinsam
  • Aussergerichtliche Einigung

IT Verträge

  • Vertrauen ist extrem wichtig
  • Klare, einfache Verträge sind wichtig
  • An das Ende der Zusammenarbeit denken

Anforderungen

  • Vertrauen
  • Austausch zwischen den Parteien / Kommunikation
  • Ideen
  • Umfang des Projektes

Beweisbarkeit

Es gilt:

  • Was die Parteien tatsächlich gelebt haben.
  • Was wie bereits vor der Vertragsunterzeichnung besprochen / zugesichert haben.

Softwareentwicklung

Klassisch

Pflichtenheft, Milestones & Abnahme = Werkvertrag + viiiel Zeit + Lizenz / Kaufvertrag

## Agile Softwareentwicklung

= Werkvertrag? (Resultat zählt)

= Auftrag? (Dienstleistung / Zusammenarbeit zählt)

= Einfache Gesellschaft? (Zusammenarbeit für das Erreichen eines bestimmten Zwecks)

Nichts passt wirklich - daher müssen die Verträge individuell zusammengestellt werden

### Vorgehen

Phase 1: 1,2,3,4

Phase 2: 5,6,7,8,9,10

  • Genügend Zeit in Phase 1 investieren

Vertragsgestalltung

Konfliktmanagement

  • Passiert in allen Softwareprozessen
  • Etwas steuern

State Of the Art

  • Flexibilität & Abänderbarkeit des Vertragsverhältnisses

Offerte & Vertragsschluss

Art. 1 OR: Vertrag kommt (nur) durch gegenseitige, übereinstimmende Willensäusserung zustande. Diese kann ausdrücklich oder stillschweigend erfolgen.

Offerte = verbindlicher Antrag, den Vertrag unter bestimmten Bedingungen (Preis, Menge etc.) abschliessen zu wollen.

Akzept = Annahme der Offerte.

Willensäusserung = schriftlich, mündlich oder konkludentes Verhalten

Unlauterer Wettbewerb

  • Hat ein Prospekt einen Einfluss auf die Willensbildung, dann ist der Preis rechtsgültig.
  • Falsche Angaben von Liefermengen

Wird keine Auftragsbestätigung gesendet, dann ist das nur eine Einladung zur Offerstellung und ist somit nicht Rechtsgültig.

Vertragsarten

Nominatverträge

Gesetzlich geregelte Vertragsformen - zwingende oder dispositive Bestimmungen: "klare Regelun""

  • Kaufvertrag (Standardsoftware, Infrastruktur)
  • Auftrag (Consulting, Installation, Entwicklung, Projektmanagement, SLA)
  • Werkvertrag (kundespezifische Software, Softwareerweiterung, Infrastruktur)
  • Miete (Hardware)
  • Zusammengesetzte Verträge (Hosting, Entwicklung, Projektvertrag, Lizensierung)

Gemischte Verträge

Zusammengesetzt aus Nominatverträgen - aus dem Gesetz wird das passende genommen - oder später angewendet...

Innominatverträge

Gesetzlich NICHT geregelte Vertragsformen. Das Prinzip der Vertragsfreiheit ermöglicht neue, innovative Vertragsinhalte & Zusammenarbeitsformen.

  • Leasingvertrag
  • Lizenzvertrag
  • Factoring-Vertrag
  • Escrow-Agreement
  • Software-Entwicklungsvertrag
  • Service Level Agreement (SLA)
  • Non Disclosure Agreement (NDA) / Non Solicitation Agreement (NSA)

Schwierige Fälle

  • Verzögerung in der Leistungserstellung
  • Mehrere an der Leistungserfüllung Beteiligte
  • (angeblich) keine oder zu späte Lieferung
  • Fehlerhaftes Produkt / Garantieleistung
  • Unklares Abnahmeverfahren
  • Fehlende Zahlung
  • Übermässige Bindung
  • andere Mängel (Nichtigkeit, Unmöglichkeit, Übervorteilung, etc.)

Wenn es schwierig wird - "Papier" (Beweismittel) produzieren

ITS

TLS (Transport Layer Security)

Secure Communication between applications

Properties

  • Authenticated
  • Integrity Protected
  • Confidential
  • Secure against replay and deletion

Building Blocks

  • Strong block ciphers (AES, Twofish, ...)
  • Authenticated encryption mpdes (GCM, CCM)
  • Diffie-Hellman Key agreement
  • Groups that yield small keys and fast DH operations (ECC)
  • Public key authentication with certificates
  • Authentication trough signatures
  • Cryptographic hash functions for key generation (HKDF) (e.g. AES session keys)

A cipher suite determinces the set of cryptographic algrogithms to be used.

Layer

  • TLS works on top of TCP
  • Can be implemented in user space (directly in the application software)
  • Does not have to worry about lost/retransmitted data

Protocol stack

TLS 1.3 Record Protocol

TLS Record Protocol

Defines the TLS packet format; all data that are using TLS are transported within TLS Records

Handshake Protocol

Used to establish TLS sessions

Change Cipher Spec Protocol (deprecated)

Dummy messages only. In TLS 1.2, it indicated switching to the newly negotiated secure communication relationship and keys

Alert Protocol

Indicates warnings/errors (e.g., certificate expired)

Application Data

Data meaningful to applications (not for TLS)

TLS message formats

Phases

Handshake

Authentication and establishment of cryptographic algorithms and key material

Data Exchange

Exchange protected data

Connection teardown

Disconnect safely

VPN - Virtual Private Network

IPSEC

  • Works on layer 3

ESP-Protected packets with sequence numbers

  • As TLS is based on TCP records cannot arrive in the wrong order unless theres an attack or a serious error has happened -> clsoe with error

Open VPN

  • Runs on top of UDP or TCP
  • Usually uses UDP, as TCP over TCP results in poor performance
  • Software on the VPN endpoints run in the user space
  • Protect IP Packets between the VPN endpoints
  • Uses TLS handshake protocol for mutual authentication and key exchange

Protocol Stack

Packet format

Wireguard

  • Works on layer 3
  • Very little configurability, deliberately no cipher and protocol agility
    • much smaller attack surface
  • uses modern crypto primitives (ChaCha20, Ruve25519)
  • Has a 1-RTT handshake
  • Has perfect forward secrcy (PFS)
  • Has DoS mitigation techniques

Message Flow

MLDM

Data Processing

In Machine Learning werden Milliarden an einzelnen Daten benötigt.

Daten als Grundlage müssen verstanden sein.

Quellen

  • Sensoren
  • Umfragen
  • Simulationen
  • Social Media
  • Texte
  • Finanzen
  • Multimedia
  • ERP System Data

Datentypen

Nominal Categorical Data

Benutzt für Bezeichnungen, die ungeordnet sind

  • Haarfarbe
  • Geschlecht

Ordinal Categorical Data

Geordnete Bezeichnungen

  • Rang
  • Bewertung in Sternen

Continous Numerical Data

Werte die gezählt werden können

  • Anzahl Personen in einem Raum

Discrete Numerical Data

Interval Daten, werden oft gemessen.

  • Exakte Menge ([0, 20])

Datenklassen

  • Eindimensionale Daten
  • Mehrdimensionale Daten
  • Netzwerkdaten
  • Hierarchische Daten
  • Zeitserien
  • Geographische Daten

Struktur

### Strukturiert

  • Datenmodelle

  • csv

  • ods

  • xlsx

  • HDF (Hierachical Data Format)

Nicht strukturiert

  • Hat kein fixes Format
  • Hat keine Struktur

Metadata

Beschreibende Daten zu Bildern

Data Pre Processing

Shit in - Shit out

Probleme

  • Schreibfehler in Quellen
  • Falsches Format von Daten
  • Falsche Berechnung von Eingabedaten
  • Verschiedene Klassifizierung von gleichen Inhalten
  • Doppelte Inhalte in falschen Werten

Methodiken

Vektorvergleich

  • Titeln: Die Levensteindistanz gibt an, wie viele Operationen notwendig sind, um von einem Text auf den anderen zu kommen.

#### Vektoren

CoSine Similiarity

### Fehlende Daten

  • Interpolation / Annahme durch andere Werte
  • Löschen / nicht zu oft, um Mengen nicht zu verfälschen

Cost Function

Gradient Descent

Generic Gradient Descent Algorithm

  • Repeat until covergence

Learning Rate / Schrittgrösse:

for every j=1..n

Learning Rate

  • Zu Beginn grosse Lernrate, um möglichst schnell ans Minimum zu gelangen

### Regeln

  • Learning Rate too small -> slow convergence
  • Learning Rate too large -> might "jump too far", might not converge or even diverge

### Learning Rate Optimization / Decay

Momentum as Learning Rate Optimizer

Idea: add a fraction of previous update direction to the update:

Effect: pushes the "jumps" of SGD in general direction towards the minimum

Regularization

  • Ist klein, kann gross, somit kann das Polygon möglichst genau den Werten enstsprechen.
  • wird nicht beeinflusst.

Hypothesis:

Cost Function:

Grosses

  • Es wäre eine Gerade
  • Die Gerade wäre möglichst flach bei 0

Hyperparameter Tuning

Hyperparameter sind Einstellungen oder Konfigurationen, die vor dem Training eines maschinellen Lernalgorithmus festgelegt werden müssen. Sie steuern Aspekte des Lernprozesses und beeinflussen, wie ein Modell trainiert wird. Im Gegensatz zu den Modellparametern, die während des Trainings aus den Daten gelernt werden, werden Hyperparameter manuell ausgewählt und können die Leistung und das Verhalten eines Modells erheblich beeinflussen. Beispiele für Hyperparameter sind Lernrate in neuronalen Netzen, Tiefe eines Entscheidungsbaums in Entscheidungsbäumen und die Anzahl der Cluster in k-means Clustering. Die Auswahl geeigneter Hyperparameter ist oft ein wichtiger Schritt beim Entwickeln von Machine-Learning-Modellen.

A hyperparameter in machine learning is a configuration setting that is external to the model and whose value cannot be learned from the data. Examples include learning rate, batch size, and the number of hidden layers in a neural network. Tuning hyperparameters is crucial for optimizing a model's performance.

Reinforcement Learning

  • Der Comnputer simuliert die Umgebung. In dieser passiert etwas.
  • Der Agent probiert durch viele Episoden, ob etwas funktioniert.
  • Der Interpreter gibt dem Agenten ein Reward, was den Agenten fördert, positive Schritte zu wählen.

Ziel: maximieren der Rewards

Supervised Machine Learning

Labels werden Beispieldaten manuell hinzugefügt.

Klassifizierung / Classification

Es wird etwas in eine Kategorie klassifiziert.

Regression

Es wird ein Wert anhand vieler Werte errechnet.

Unsupervised Machine Learning

  • Muster in einer Menge an Daten finden
  • Gruppierungen in einer Menge an Daten finden

Dimensional Reduction

Daten werden aus hochdimensionalen Daten zu 2D umgewandelt. Diese sind für den Menschen einfacher zu verstehen.

Association Rule Mining

Zusammenhänge zwischen Produkten werden gesucht.

  • Wo sollten Produkte optimal platziert werden
  • Welche Produkte werden oft gemeinsam gekauft

Outlier Detection

Findet Dinge die komplett von anderen abweichen.

Regression

Univariate

Statistische Auswertung nur eines Merkmals. Beispiel: für zehn Personen wird das Körpergewicht gemessen und dann ein Mittelwert bzw. Durchschnitt gebildet. Statt univariat könnte man auch eindimensional sagen.

K Nearest Neighbour Regression

In den meisten Fällen haben nah beieinander liegende Datenpunkte auch ähnliche Kategorien / Werte.

  • Es gibt keine theoretische spezifikation wie viele k Nachbaren verwendet werden sollen.
  • Je grösser k ist, desto geringer ist es für das Model "lokale" patterns zu erkennen, jedoch wird die Varianz kleiner und die Prediction wird stabiler.
  1. Es werden K Datenpunkte verwendet, die dem Suchwert am nähesten liegen.
  2. Mittelwert der K Punkte ergibt annäherung.

Linear Regression

Die Daten sind linear, es gibt also eine Linie. Diese kann dafür verwendet werden, anhand einer Gerade einen Wert Y zu dem Wert X zu bestimmen.

  • : Schnittpunkt mit der Y-Achse
  • : Steigung der Gerade

Loss

Abstand von einem Sample Wert zu seinem richtigen Wert

Wird pro einzelnes Samples berechnet

Cost

Abstand aller Samples zu ihrem korrekten Wert.

Beispiel

 X Y
1.001.00
2.00 2.00
3.001.30
4.003.75
5.002.25

Eigenschaften

Linearity: Die Abhängigkeit zwischen X und Y muss linear sein.

Homoscedasticity: Die Varianz ist für alle Werte gleich

Independence: Der Ausgangswert ist unabhängig der Eingabewerte

Normality: Glockenkurve der Varianz

Linear Regression

Closed Form

Funktioniert nur mit univarianten Daten. Beruht auf der Ableitung von Varianzen.

Normal Equation

Lässt sich für alle Probleme anwenden.

Runtime

  • Mehr als 15'000 Features sind nicht möglich.

Matrix X ist:

Umgewandlet, bedeutet dass: , wobei N die Features sind. Die Matrix wird dadurch riesig.

Cost function

A cost function s a a measure, of how well a machine learning model performs by quantifiying the difference between predicted and actual outputs.

Die Werte werden quadriert, um:

  • Die Werte zu normieren
  • Grosse Fehler werden verstärkt

### Optimaler Wert der Cost-Function

  • Die Cost-Function ist genau dann 0, wenn alle Punkte genau auf einer Gerade sind.

  • Der Fehler der Kostenfunktion soll so klein sein wie möglich, der Wert wird dabei einfach minimiert.

Mit der Normal Equation können wir die optimalen Theta Werte bestimmen.

## Hypothesis

Polynomial Regression

Künstliche Hypothese

polynomial_features = PolynomialFeatures(degree=2)
x_poly = polynomial_features.fit_transform(X_train)

Grad 2

Variable und haben folgende Optionen:

Grad 3

Variable und haben folgende Optionen:

Clasification

The training data comes in pairs. Yet the target values belong to a finite set. The goal is to pick the correct one as often as possible.

  • Analyzing a text to determine if the sentiment is happy or sad.
  • Using hockey players' statistics to predict which of two teams will probably win a match.
  • Analyzing a song to determine if it is classical, techno, pop, or rock.

Binary Classification

There are only two classes to distinguish, typically "positive" or "negative".

Logistic Regression

If x is a Vector

Training

Loss Function

Cost Function

Evaluation

  • Comparing training and testing result. If training accuracy is nearly 100%, it is overfitting.
  • Since testing data is new data, it is used to test the accuracy on completely new data

Confusion Matrix

T = True F = False

Precision: TPos / (TPos + TNeg + TNeu)

Recall: TPos / (TPos / FNeg / FNeu)

F-Measure: 2*precision * recall (precision + recall)

Accuracy: (all true) / (all data)

SVM (Support Vector Machines)

Tries to find the "w" and "b" value in a way that maximizes the seperation between different classes of data points.

Weight Vector (w)

  • Tells how much each feature matters

Bias Term (b)

  • Is like an anchor point for the decision boundary. It shifts the boundary away from the origin

Classification

The values in the context of Support Vector Machines (SVM) represent the decision function values for individual data points. These values can provide insights into how confidently and correctly the SVM classifies these data points. Here's how to interpret them:

  1. Positive Values of :

    • If , it means the SVM confidently classifies the data point as belonging to the class with a positive label. The larger the positive value, the higher the confidence in the classification.
  2. Negative Values of :

    • If (y^{(m)} < 0), it indicates the SVM confidently classifies the data point as belonging to the class with a negative label. The more negative the value, the higher the confidence in this classification.
  3. Near 0:

    • If is close to 0, it suggests that the data point is located near the decision boundary. In this case, the SVM may be less certain about the classification.
  4. Magnitude of :

    • The magnitude of reflects the confidence level. Larger magnitude values indicate higher confidence in the classification, whether positive or negative.

So, you can use values to understand how well the SVM has classified individual data points. Large positive or negative values indicate strong confidence, values near 0 suggest proximity to the decision boundary, and the sign of the value indicates the predicted class.

Calculate distance

import numpy as np

# Assuming you have the weight vector 'w' from your trained SVM model
w = model.coef_[0]

# Calculate the margin width
margin_width = 2 / np.linalg.norm(w)

print("Margin Width:", margin_width)

## Scatterplot generieren

import numpy as np
import matplotlib.pyplot as plt

# Assuming you have the feature data 'X' and class labels 'y'
# Also, the trained SVM model with optimized parameters: model, model.intercept_, model.coef_

# Scatterplot of data points
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired, s=30)

# Decision boundary
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()

# Create a grid of points
xx, yy = np.meshgrid(np.linspace(xlim[0], xlim[1], 50),
                     np.linspace(ylim[0], ylim[1], 50))

# Calculate decision function values for the grid points
Z = model.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

# Plot decision boundary
plt.contour(xx, yy, Z, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])

# Highlight the support vectors
plt.scatter(model.support_vectors_[:, 0], model.support_vectors_[:, 1],
            s=100, facecolors='none', edgecolors='k')

plt.title('SVM Decision Boundary with Gutters')
plt.show()

## Calculate accuraccy

from sklearn.model_selection import cross_val_score
from sklearn.svm import SVC

# Create an SVM model with your preferred parameters
model = SVC(kernel='linear', C=1.0)

# Assuming you have feature data X and labels y

# Use cross_val_score to calculate accuracy
accuracy_scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')  # cv=5 for 5-fold cross-validation

# Calculate the mean accuracy
mean_accuracy = accuracy_scores.mean()

print("Mean Accuracy:", mean_accuracy)

Hyperplane

Standardization and Normalization

Ist wichtig, wenn die Features numerisch sehr unterschiedlich sind.

# Neural Networks

Neuron

Applies an activation function to the weighted sum of its inputs plus a bias term.

Deep Neural Networks

Are Neural Networks with more than one hidden layer.

Activation Functions

An activation function in neural networks introduces non-linearity to the model by determining the output of each neuron. Common activation functions include ReLU (Rectified Linear Unit) and Sigmoid, helping the network learn complex patterns and relationships in data during training.

## Softmax

Goal: For Classification tasks with mutually exclusive classese, in the output layer we want to have 1.0 for the correct class and 0 for all other classes.

Training in Feedforward Neural Networks

SCAD

The idea of SCAD is to simplify the development, hence the engineers

Serverless Computing Paradigm

Optimal Use Cases

  • Single action repeats every time the user clicks on something
  • Usage of services vary

Developer Mindset

  • Serverless architectures are application designs stat ... remove much of the need for a traditional always-on server component
  • Serverless architectures may benefit from significantly reduced operational cost, complexity, and engineering lead time
  • Serverless architecture comes at a cost of increased reliance on vendor dependencies and comparativately immature supporting services

Formal Definition

Serverless computing is a cloud computing paradigm offering a high-level application programming model that allows one to develop and deploy cloud applications WITHOUT allocatign and managing virtualized serves and resources or being concerned about other operational aspects. The responsibility for operational aspects, such as fault tolerance or the elastic

Architekturen

XaaS Everything as a Service

Application-specific

SaaS - Software

DaaS - Data

DBaaS - Database

Cloud Services

IaaS - Infrastrucutre

PaaS - platform, CaaS - Containers

Serverless - FaaS, (+CaaS), BaaS

Function as a Service (FaaS)

Stateless & Shortlived

high elastic scalability

triggered by external events

single functionality & responsiblity

pay per use

Function can for example run in a CaaS (Container)

Backend as a Service (BaaS)

FaaS hat nur Computing Ressourcen (fast) kein Storage.

Daher BaaS welche stateful ist mit low latency

Dafür zusätliche kosten.

Databases, Message brokers …

Platform as a Service (PaaS)

  • Long Lived
  • More Flexible resource models
  • Language constrained

Container as a Service (CaaS)

  • Long Lived
  • More flexible resource models
  • unconstrained runtime
  • "serverless CaaS": short-lived, resource constraints
  • often underlying PaaS/FaaS, especially "FaaS with contaniers"

CaaS

LXC

Background

  • vendor neutral,
  • linux specified

Booting

  • from a directory, e.g. unpackaged image
/config     -> text file for e.g. mounts, capabilites, networks, ...
/rootfs     -> /bin /dev/ etc /home

Namespacing of system resources

  • file systems, users, processes, network interfaces, ...
  • in the linux kernel

Application integration

  • native python library
  • frontends such as Flockport
import lxc

c = lxc.Container("myubuntucontainer")

c.create("download", 0, {
    "dist": "ubuntu",
    "release": "xenial",
    "arch": "amd64
    })
# Downloadint the rootfs... (~400 MB; alpine would be ~7 MB)
# -> True

c.running
# -> False

c.start()
# - True

import os
def f(): os.system("ps waux; ls -l /home")

c.attach_wait(f)
# - pid

Rocket

Docker

Background

  • Docker inc
  • Silicon Valley

Booting

  • from a Docker container image with a layered file system
  • images created by integration of Dockerfiles

Namespacing of system resources

  • as processes through central daemon (or daemon-less, e.g. Podman)

Application integration

  • composition and distributed computing tools (Docker Compose & Swarm)
  • diverse networkign models, e.g. host mode, NAT bridge
  • pass-through access to hardware via device files, e.g. GPU, USB
  • access to local file system through volumes
FROM python:3

RUN pip install requests && \
    rm /usr/lib/python3.6/site-packages/requests/__init__.py && \
    wget https://blalbla.deb && \
    dpkg -i blabla.deb

ADD my-application /srv/my-application
WORKDIR /srv

EXPOSE 8000

ENTRYPORT ["/usr/bin/dumb-init", "--"]
CMD ["/srv/my/application/start.sh]

Build Dockerimage

docker build -t mycontainer

View images

docker images

Run container

docker run -p 8080:8080 -it --rm \
    -v /var/www/html/:/usr/local/tomcat/webapps/news \
    mycontainer

Debug

  • docker ps
  • docker logs
  • docker exec

Docker Compose

Compose files (docker-compose.yaml) describe

  • a set of microservices, implemented as Docker continers
  • build and run configuration
  • dependencies
  • isntance details: replicas, restart policies, placement, networking, volumes

Compose tool

  • canonical command: docker-compose up
  • in contrast docerk-compose down --volumes
  • log merging
  • command execution per instance

Helper Tools

  • service dependencies (-> V11): shell script wait-for-it

Web Architektur

Internet

  • Weltweites Netzwerk, dass aus vielen Rechnernetzwerk besteht.
  • Urprünglich: ARPANET (1969: vier Knoten)
  • Als Internet ab 1987 bezeichnet (ca. 27'000 Knoten)

Technologien

Clientseitig (Frontend)

  • Beschränkt auf das, was der Browser kann
  • HTML, CSS, JavaScript, einige weitere Sachen

Serverseitig (Backend)

  • Praktisch unbeschränkt: Plattform, Programmiersprache, ...
  • Erzeugt und gesendet wird das, was der Browser kann

Statische Webseiten

Clientseitige Programmlogik

Serverseitige Programmlogik

Javascript

Beschreibung

  • Dynamisches Typenkonzept
  • Objektorientierter und funktionaler Stil möglich

positiv

  • Mächtige unde moderne Sprachkonzepte
  • Leistungsfähige Laufzeitumgebungen

negativ

  • Design Mängel aus den Anfangstagen
  • Grundlegende Änderungen nicht möglich, da nur Erweiterungen möglich sind

Standards

  • ECMAScript
  • versionen
    • ES3: 2000 - 2010 (verbreitete Version)
    • ES4: Übung 2008 abgebrochen
    • ES5: 2009, kleineres Update
    • ES6: 2015, umfangreiche Neuerungen
    • ES7, Javascript 2016
    • Nun jährliche Updates
  • Transpiler: Babel

Console

### Formatiertes ausgeben

let x = 30;
console.log("the value of x is ", x);
// -> the value of x is 30

console.log("my %s has %d ears", 'cat' 2);
// -> my cat has 2 ears

Konsole löschen

console.clear()

Stack trace ausgeben

Wird bei Fehlern oft automatisch ausgegeben.

console.trace()

Zeit messen

console.time()
console.log("Welcome to Programiz!");
console.timeEnd()
// default: 3.572ms

### Fehler ausgeben

console.error()

Zahlen

  • Zahlentyp in JavaScript: Number
  • 64 Bit Floating Point entsprechend IEEE 754 (double in Java)
  • Enthält alle 32 Bit Ganzzahlen
  • Konsequenz alle Java int auch in JavaScript exakt dargestellt
  • Weitere Konsequenz: oft Rechnunggenauigkeit mit Zahlen mit Nachkommastellen

## Zahlenliterale

| Typ | Beispiel | | Ganzzahlliteral | 17 | | Dezimalstellen | 3.14 | | Dezimalpunktverschiebung (*10^x) | 2.998e8 |

Nicht exakte Zahlen

0.1 = 0x3FB999999999999A, entspricht nicht exakt 0.1 0.25 // hexadezimal 3FD0000000000000, entspricht exakt 0.25

Ausdrücke

  • Rechenoperationen wie in Java
  • Spezielle Zahlen: Infinity, -Infinity, NaN
100 + 4 * 11 		// 114
(100 + 4) * 11 		// 1144
314 % 100 			// 14

1/0					// Infinity
Infinity + 1		// Infinity
0/0					// NaN (Not a Number)

BIGINT

  • Mit ES2020 eingeführt
  • Lieterale mit angehängem n
  • Keine automatische Typumwandlung von/zu Number
1n + 2n				// 3n
2n ** 128n			// 340282366920938463463374607431768211456n

BitInt(1)			// 1n
Number(1n)			// 1

1n + 1				// Cannot mix BigInt and other types

Typeof

  • Operator, der TypString seines Operanden liefert
typeof(12)			// 'number'
typeof(2n)			// 'bigint'
typeof(Infinity)	// 'number'
typeof(NaN)			// 'number'
typeof('number')	// 'string'

## Strings

  • Sequenz von 16-Bit-Unicode-Zeichen
  • Kein spezieller char-Typ
  • Strings mit "..." und '...' verhalten sich gleich

String-Verkettung

`con` + "cat" + 'enate' // 'concatenate'

Escape Sequencen

SequenzBedeutung
\\Backslash
\nnewline
\rcarriage return
\vvertical tab
\bbackspace
\fform feed
\uXXXX (4 Hexziffern) UTF-16 code unit
\xXX (2 Hexziffern)ISO-8859-1 character
\XXX (1-3 Oktalziffern) ISO-8859-1 character

Template Strings

  • \ wird als \ interpretiert (Ausnahme vor `, $ und Leerzeichen)
  • Kann Zeilenwechsel enthalten
  • String-Interpolation: Werte in String einfügen
`half of 100 is ${100 / 2}`		// half of 100 is 50
`erste Zeile
zweite Zeie`					// 'erste Zeile\nzweite Zeil'

Logische Ausdrücke

  • Typ boolean mit Werten: true / false
  • Vergleiche liefern Ergebnis vom Typ boolean
  • Logische Operatoren entsprechen denen in C und Java
  • Strings sind Werte: Vergleich mit == ist kein Problem
typeof(true)				// 'boolean'
3 > 4						// false
1 < 2 && 2 < 3				// true
4 >= 5 || !(1 == 2)			// true
"ab" == "a" + "b"			// true

NaN == NaN					// false

Spezielle Werte

null 						// null
undefined					// undefined

let wert					// wert = undefined
wert						// undefined

Nodejs

Ist nicht die einziege Javascript-Laufzeitumgebung ausserhalb von Browsern. Weitere Alternativen sind:

  • Deno (https://deno.land)

  • Bun (https://bun.sh)

  • Asynchrone, ereignisbasierte Javascript-Laufzeitumgebung

  • Grundlage für skalierbare Netzwerk-Anwendungen

  • Basiert auf Googles V8 Engine

  • Open Source und plattformübergreifend

Beispiel

// ==== hello-world.js ====
const http = require('http');

const hostname = '127.0.0.1';
const port = '3000';

const server = http.createServer((req, res) => {
	res.statusCode = 200;
	res.setHeader('Content-Type', 'text/plan');
	res.end('Hello, World!\n');
});

server.listen(port, hostname, () => {
	console.log(`Server running at http://${hostname}:${port}/`);
});

Verwendung

Startet das Script, zu dem der Pfad führt

$ node hello-world.js
Server running at http://127.0.0.1:3000/
# Abbruch mit CTRL-C

Startet eine interaktive REPL (Read Eval Print Loop) Shell gestartet werden

$ node
Welcome to Node.js vs.12.16.2
Type ".help" for more information.

Frameworks

  • Node-js ist eine low-level Plattform
  • Zahlreiche Frameworks und Tools bauen darauf auf

Beispiele

  • Express: Webserver, Nachfolger: Koa
  • Socket.io: Echtzeitkommunikation
  • Next.js: serverseitiges React Rendering
  • Webpack: JavaScript Bundler
  • Babel, TypeScript
  • uvm.

NPM

  • Paketverwaltung für Node.js
  • Repository mit > 1 Mio Paketen
  • Werkzeuge zum Zugriff auf das Repository (npm, yarn)
  • Seit 2020: Github / Microsoft

Objekte

## Objektliterale

let person = {
    name: "John Baker",
    age: 23,
    "exam rsults": [5,5, 5.0, 5.0, 6.0, 4.5]
}

Zugriff

console.log(person.name)
console.log(person["exam results"])

Zugriff auf nicht vorhandenes Attribut liefert "undefined"

Optional


console.log(person.age.month)  // TypeError
console.log(person.age?.month)  // undefined 

Erweitern

person.lastname = "Rüdiger"

Entfernen

let obj = {
    message: "ready",
    ready: true,
    tasks: 3
}

delete obj.message

Attribute in Objekt

let obj = {
    message: "ready",
    ready: true,
    tasks: 3
}

"message" in obj // true

Methoden


let cat = {
    type = "cat",
    sayHello: () => "Meow"
}

cat.sayHello() // Meow

## Interner Zugriff


let cat = {
    type = "cat",
    sayHello() { "Meow from " + this.type }
}
  • Pfeilnotation funktioniert nicht

## Analyse

Keys

let obj = {a:1, b: 2}

Object.keys(obj)

Values

let obj = {a:1, b: 2}

Object.values(obj)

Zusammenführen

let obj1 = {a:1, b: 2}
let obj2 = {c:1, d: 2}
let obj3 = {e:1, f: 2}

Object.assign(obj1, obj2)

Object.assign(obj1, obj2, obj3)

let objCopy =  Object.assign({}, obj1)

objCopy == obj1 // false

Spread Syntax

let obj1 = {a:1, b: 2}
let obj2 = {c:1, d: 2}

{...obj1, ...obj2, f:4}

Objekte Destrukturieren

let obj = {a:1, b: 2}

let {a, b} = obj

console.log(a) //1

Arrays

  • Können von beliebigen Typen sein
  • Typen können gemischt werden

Is Array

let data = [1, 2, 3]
typeof(data)    // 'object'
Array.isArray(data)     // true

Methoden

Hinzufügen

let data = [1, 2, 3]
data.push(10)   // 4

data.push(11, 12)   // 6

Letztes Element löschen

let data = [1, 2, 3]
data.pop(10)   // 36

### Shift

Entfernen am Array Anfang

let data = [1, 2, 3]
data.shift()   // 1

console.log(data)   // [2, 3]

Unshift

Entfernen am Array Anfang

let data = [1, 2, 3]
data.unshift(5)   // 4

console.log(data)   // [5, 1, 2, 3]

Erstes Element in Array finden

let data = [1, 2, 3, 1]
console.log(data.indexOf(1))   // 0

Letzes Element in Array finden

let data = [1, 2, 3, 1]
console.log(data.lastIndexOf(1))   // 3

### Bereich eines Arrays ausschneiden

let data = [1, 2, 3, 1]
data.slice(1, 3)       // [2, 3]

Arrays zusammenhängen

let data = [1, 2, 3, 1]
data.concat([100, 101])     //[1, 2, 3, 1, 1000, 101]

Iterieren

Standard for schleife

for (let i=0; i < myArray.length; i++) {
    console.log(myArray[i]);
}

Einfachere Variante

for (let item in myArray) {
    console.log(item);
}

Spread-Syntax

let parts = ["shoulders", "knees"]

["head", ...parts, "and", "toes"]   // ["head", "shoulders", "knees", "and", "toes"]

Destrukturieren

let numbers = [1,2,3]
let [a,b,c] = numbers

console.log(c)      // 3

Abbilden

let num = [5, 2, 9, -3, 15, 7, -5]

num.map(n => n*n)   // [25, 4, 81, 9, 225, 49, 25]

Reduzieren

let num = [5, 2, 9, -3, 15, 7, -5]

num.reduce((curr, next) => curr + next) // 30

Funktionen

Funktion definieren

const squareV1 = function(x) {
    return x * x;
}

function squareV2(x) {
    return x*x;
}

const squareV3 = x => x * x;

Gültigkeitsbereiche

let     m = 10      // block scope
const   n = 10      // constant with block scope
var     p = 10      // variable with function scope

Globale Variablen

  • Ausserhalb von Funktionen definiert
  • In Funktionen kein const, let, var
  • Gültigkeitsbereich möglichst einschränken
result = 1 + 2

## Parameter

Parameter sind standardmässig undefined, somit müssen nicht alle Parameter gesetzt werden.

Rest-Parameter

function max ([...numbers]) {
    let result = -Infinity;
    for (let number of numbers) {
        if (number > result) {
            result = number;
        }
    }

    return result
}

console.log(max(4, 1, 9, -2))

Alternative

function max () {
    let result = -Infinity;
    for (let number of arguments) {
        if (number > result) {
            result = number;
        }
    }

    return result
}

console.log(max(4, 1, 9, -2))

Dekorieren

function trace (func) {
    return (...args) => {
        console.log(args)
        return func(...args)
    }
}

let factorial = (n) => (n <= 1) ? 1 : n * factorial(n-1)

factorial = trace(factorial)

console.log(factorial(3))

Prototypen

function speak (line) {
    console.log(`The ${this.type} rabbit says '${line}'`)
}

let whiteRabbit = {type: "white" , speak}
let hungryRabbit = {type: "hungry", speak}

hungryRabbit.speak("I could use a carrot right now.")

// → The hungry rabbit says 'I could use a carrot right now.

Strict Mode

"use strict"

function speak(line) {
    console.log(`The ${this.type} rabbit says '${line}'`)
}

speak("I could use a carrot right now.")

// -> TypeError 'Cannot read property type of undefined'

Call / Apply

function speak (line) {
    console.log(`The ${this.type} rabbit says '${line}'`)
}

let hungryRabbit = {type: "hungry", speak}

speak.call(hungryRabbit, "Burp!")  // The hungry rabbit says 'Burp!'   
speak.apply(hungryRabbit, ["Burp!"])   // The hungry rabbit says 'Burp!'

Bind

function speak (line) {
    console.log(`The ${this.type} rabbit says '${line}'`)
}

let hungryRabbit = {type: "hungry", speak}

let boundSpeak = speak.bind(hungryRabbit)

boundSpeak("Burp!")   // The hungry rabbit says 'Burp!'   

Lambdas

let test = []
test.map(n => n**2)

Prototype Chain

var Dragon = function(location){  
    /*
     * <Function>.call is a method that executes the defined function,
     * but with the "this" variable pointing to the first argument,
     * and the rest of the arguments being arguments of the function
     * that is being "called". This essentially performs all of
     * LivingEntity's constructor logic on Dragon's "this".
     */
    LivingEntity.call(this, location);
    //canFly is an attribute of the constructed object and not Dragon's prototype
    this.canFly = true;
};

/*
 * Object.create(object) creates an object with a prototype of the
 * passed in object. This example will return an object
 * with a prototype that has the "moveWest" and "makeSound" functions,
 * but not x, y, or z attributes.
 */
Dragon.prototype = Object.create(LivingEntity.prototype);

/*
 * If we didn't reset the prototype's constructor
 * attribute, it would look like any Dragon objects
 * were constructed with a LivingEntity constructor
 */
Dragon.prototype.constructor = Dragon;

/*
 * Now we can assign prototype attributes to Dragon without affecting
 * the prototype of LivingEntity.
 */
Dragon.prototype.fly = function(y){  
    this.y += y;
}

var sparky = new Dragon({  
    x: 0,
    y: 0,
    z: 0
});  

Klassen

class Person {
    constructor (name) {
        this.name = name
    }

    toString() {
        return `Person with name '${this.name}'`
    }
}

let john = new Person("John")
console.log(john.toString())    // Person with name 'John'

Vererbung

class DateFormatter extends Date {
  get FormattedDate() {
    const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    return `${this.getDate()}-${months[this.getMonth()]}-${this.getFullYear()}`;
  }
}

console.log(new DateFormatter('August 19, 1975 23:15:30').FormattedDate);
// Expected output: "19-Aug-1975"

Properties


class Person {
    constructor(name) {
        this.name = name;
    }
    get name() {
        return this._name;
    }
    set name(newName) {
        newName = newName.trim();
        if (newName === '') {
            throw 'The name cannot be empty';
        }
        this._name = newName;
    }
}

let p1 = new Person()
p1.Name = "hey"

console.log(p1.Name) // hey

# Webserver

Port Service
20FTP - Data
21 FTP - Control
22SSH Remote Login Protocol
23 Telnet
25 Simple Mail Transfer Protocol (SMTP)
53Domain Name System (DNS)
80HTTP
443HTTPs

HTTP Requests

 MethodeBeschreibung
GET Resource Laden
 POSTInformation senden
PUT Resource anlegen, überschreiben
PATCHRresource anpassen
DELETEREsource löschen

HTTP Response Codes

Code Beschreibung
1xxInformation (101 switching protocols)
2xxErolg (200 OK)
3xxWeiterleitung (301 moved permanently)
4xx Fehler in Anfrage (403 Forbidden, 404 Not Found)
5xxServer-Fehler (501 Not implemented)

Einfacher Web Server

const {createServer} = require("http")
let server = createServer((request, response) => {
  response.writeHead(200, {"Content-Type": "text/html"})
  response.write(`
    <h1>Hello!</h1>
    <p>You asked for <code>${request.url}</code></p>`)
  response.end()
})
server.listen(8000)
console.log("Listening! (port 8000)")

Einfacher Web Client

const {request} = require("http")

let requestStream = request({
    hostname: "eloquentjavascript.net,
    path: "/20_node.html",
    method: "GET",
    headers: {Accept: "text/html"}
}, response => {
    console.log("Server responded with status code:", response.statusCode);
})
requestStream.end()

Streams

Server

const {createServer} = require("http")
createServer((request, response) => {
  response.writeHead(200, {"Content-Type": "text/plain"})
  request.on("data", chunk =>
    response.write(chunk.toString().toUpperCase()))
  request.on("end", () => response.end())
}).listen(8000)

Client

const {request} = require("http")
let rq = request({ hostname: "localhost", port: 8000,
method: "POST"
}, response => {
  response.on("data", chunk =>
    process.stdout.write(chunk.toString()));
})
rq.write("Hello server\n")
rq.write("And good bye\n")
rq.end()

File Server


const {createServer} = require("http") const methods = Object.create(null)
createServer((request, response) => {
let handler = methods[request.method] || notAllowed; handler(request)
.catch(error => {
if (error.status != null) return error return { body: String(error), status: 500 }
    })
    .then(({body, status=200, type="text/plain"}) => {
response.writeHead(status, {"Content-Type": type}) if (body && body.pipe) body.pipe(response)
else response.end(body)
    })
}).listen(8000)

[File Server](https://eloquentjavascript.net/20_node. html#h_yAdw1Y7bgN)

Express Web Server

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
    res.send('Hello World!')
})

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
})

### Routing

app.get('/', function (req, res) { res.send('Hello World!')
})
app.post('/', function (req, res) {
  res.send('Got a POST request')
})
app.put('/user', function (req, res) { res.send('Got a PUT request at /user')
})
app.delete('/user', function (req, res) {
  res.send('Got a DELETE request at /user')
})

Statische Dateien

app.use(express.static('public'))
// http://localhost:3000/css/style.css
// Pfad zur Datei: public/css/style.css

app.use('/static', express.static('public'))
// http://localhost:3000/static/css/style.css
// Pfad zur Datei: public/css/style.css

Cookies

  • HTTP als zustandsloses Protokoll konzipiert
  • Cookies: Speichern von Informationen auf dem Client
  • RFC 2965: HTTP State Management Mechanism
  • REsponse: Set-Cookie-Header, Request: Cookie-Header
  • Zugriff mit JavaScript möglich (ausser HttpOnly ist gesetzt)

Sessions

  • Cookies auf dem Client leicht manipulierbar
  • Session: Client-spezifische Daten auf dem Server speichern
  • Identifikation des Clients über Session-ID (Cookie o.ä.)
  • GEFAHR: Session-ID gerät in falsche Hände (Session-Hijacking)
const express = require('express')
const cookieParser = require ('cookie-parser')
const session = require('express-session')
const app = express();

app.use(cookieParser())
app.use(session({secret: "Strong Secret Here!"}))

app.get('/', function(req, res) {
  if (req.session.page_views) {
    req.session.page_views++
    res.send(`You visited this page ${req.session.page_views} times`) 
  }
  else {
    req.session.page_views = 0
    res.send("Welcome to this page for the first time")
  }
})

app.listen(3000)

Fetch API


const url = "https://.."

fetch(url).then(response => {
  console.log(response.status)    // 200
  console.log(response.headers.get("Content-Type")) // text/plain

  response.text().then(text => {
    // GET TEXT
  })

  response.json().then(json => {
    // GET JSON
  })
})

DOM - Document Object Model

  • Repräsentiert die angezeigte Website

Window

  • Globales Objekt der Browser Plattofrm
  • Alle globalen Variablen und Methoden sind hier angehängt
  • Neue globale Variablen landen ebenfalls hier
window.alert === alert              // true
window.setTimeout === setTimeout    // true
window.parseInt === parseInt        // true

Represents the state and the identity of the user agent. It allows scripts to query it and to register themselves to carry on some activities.

navigator.userAgent // "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:104.0) Gecko/20100101 Firefox/104.0"

navigator.language  // "de"

navigator.platform // "MacIntel"

navigator.onLine    // true

Location

  • Aktuelle Webadresse im Browser

location.href                   // "https://gburkert.github.io/selectors/"
location.protocol               // "https:"
document.location.protocol      // "https:"

DOM Scripting


let aboutus = document.getElementById("aboutus")
let aboutLinks = document.getElementsByTagName("a")

let aboutImportant = aboutus.getElementyByClassName("important")

let navLinks = document.querySelectorAll("nav a")

Asynchron

File API

Dateinformationen

FS Stats

const fs = require('fs')
fs.stat('test.txt', (err , stats) => {
    if (err) {
        throw err
    }

    stats.isFile()  // true
    stats.isDirectory() // false
    stats.isSymbolicLink()  // false
    stats.size      // 1024000 (bytes)
})

Lesen einer Datei

const fs = require('fs')
fs.readFile('test.txt', "utf8", (err , data) => {
    if (err) {
        throw err
    }

    console.log(data)
})

Beschreiben einer Datei

const fs = require('fs')
const content = "Node was here!"
fs.readFile('test.txt', content, (err , data) => {
    if (err) {
        throw err
    }

    console.log(data)
})
FunktionBezeichnung
fs.accessZugriff auf Datei oder Ordner prüfen
fs.mkdir Verzeichnis anlegen
fs.readdirVerzeichnis lesen, liefert Array von Einträgen
fs.renameVerzeichnis umbenennen
fs.rmdirVerzeichnis löschen
fs.chmodBerechtigungen ändern
fs.chownBesitzer und gruppe ändern
fs.copyFileDatei kopieren
fs.linkErstellt einen neuen festen Link
fs.symlinkErstellt einen neuen symbolic link
fs.watchFileDatei auf Änderungen überwachen

## Calllbacks

Callbacks erlauben es Funktionen an einen EventHandler, oder anderen Funktionen mitzugeben.

document.getElementById("button").addEventListener("click", () => {
    // element of id "button" clicked
});

Set Timeout

  • Der Callback wird zu einem späteren Zeitpunkt (in ms) ausgeführt
  • Eintrag in die Timer-Liste, auch wenn Zeit auf 0 gesetzt wird
  • Kann mit clearTimeout entfernt werden
const id = setTimeout(() => {
    // runs after 50 milliseconds
}, 50);
clearTimeout(id);

Intervall

const id = setTimeout(() => {
    // runs every 2000 milliseconds = 2 seconds
}, 2000);
clearInterval(id);

### Sofort als möglich

setImmediate(() => {
    // runs as best as possible
});

Event Emitter

Auf etwas hören

const EventEmitter = require('events')
const door = new EventEmitter()

door.on('open', () => {
    console.log("Door was opened")
});

door.on('open', (speed) => {
    console.log(`Door was opened, speed: ${speed || 'unknown'}`)
});

Event auslösen

door.emit('open')
door.emit('open', 'slow')

Promises

// Funktion, die einen Promise erstellt und zurückgibt
function doSomethingAsync() {
  return new Promise((resolve, reject) => {
    // Simuliert eine asynchrone Aufgabe (z. B. eine Netzwerkanfrage)
    setTimeout(() => {
      const success = true;

      if (success) {
        resolve("Erfolg!");
      } else {
        reject("Fehler!");
      }
    }, 2000); // Simuliert eine Verzögerung von 2 Sekunden
  });
}

// Verwendung des Promises
doSomethingAsync()
  .then((result) => {
    console.log("Erfolgreich: " + result);
  })
  .catch((error) => {
    console.error("Fehler: " + error);
  });

Zustände

Zustand Beschreibung
PendingAusgangszustand
FullfilledErfolgreich abgeschlossen
 rejected Ohne Erolg abgeschlossen
  • Der Zustandswechsel kann nur einmalig passieren
  • Der Endzustand kann entweder fullfilled oder rejected sein

Promise.All

  • Erhält einen Array von Promises
  • fullfilled sobald ALLE fullfilled sind
  • Ist ein Promise rejected, dann ist das gesamte Objekt rejected

Promise.Race

  • Erhält einen Array von Promises
  • Erfüllt sobald eine davon fullfileed ist
  • Ist ein Promise rejected, dann ist das gesamte Objekt rejected

Async / Await

function resolveAfter2Seconds(x) {
    return new Promise(resolve => {
        setTimeout( () => {
            resolve(x)
        });
    });
}

async function add1(x) {
    var a = resolveAfter2Seconds(20)
    var b = resolveAfter2Seconds(30)

    return x + await a + await b
}

add(10).then(console.log)

KT

Allgemeine Formeln

BeschreibungSymbolEinheitFormel
Signaldämpfungdb
Signaldämpfung über Spannungdb
AmplitudeV
Maximale Anzahl erkennbare Zuständebit/s
Maximal erreichbare Bitrate (Hartley's Gesetz)bit/s
Signalleistung
Rauschleistung
Kanalkapazität
Nettobitrate
Maximale Bitrate
Unterscheidbare SignalzuständeAnzahl
Amplitude
Bandbreite
max. SymbolrateHz oder
Symboldauers

Log Umwandeln: =>

dB umwandeln:

OSI Schichten

Serielle Schnittstelle

OSI Referenzmodell

Verbindungsorientiert

  • Verbindungsaufbau nötig
  • Ziel muss bereit sein

Verbindungslos

  • Jederzeit Nachrichten schicken
  • Ziel muss nicht „bereit“ sein

Zuverlässig

  • Kein Datenverlust
  • Sicherung durch Fehelr-Erkennung -/korrektur
  • Text-Nachrichten

Unzuverlässig

  • Möglicher Datenverlust
  • Keine Sicherung
  • Streaming

7. Application Layer (Verarbeitungsschicht)

Anwendungen greifen auf das Internet zu

6. Presentation Layer (Darstellungsschicht)

Daten werden für die Anwendung formatiert (HTTP, Email, ...)

5. Session Layer (Session Layer)

Koltrolliert Sessions und Ports

4. Transport Layer (Transportschicht)

Transport von Daten in Protokollen (TCP, UDP, ...)

3. Network Layer (Vermittlungsschicht)

  • Paketierung von Daten / Unterteilen von Daten
  • Fragmente erstellen / zusammensetzen
  • Flusskontrolle
  • Zwischenspeichern von Daten

2. Datalink Layer (Sicherungsschicht)

  • Adressierung
  • Zugriff auf Ressourcen
  • Netzüberlastung vermeiden / Fehlererkennung

1. Physical Layer (Bitübertragungsschicht)

  • Rohe Übertragung von Bits (ungesichert)

Application Layer

DNS (Domain Name Space)

  • Leserliche Darstellung von IP-Adressen
  • Hauptdömäne ist Root (.)
 Beschreibung Domain
Fully Qualified Fomain Namebob.sw.eng.
Root.
TLD (Top Level Domain) eng
SLD (Second Level Domain)sw

TFTP (Trivial File Transfer Protocol)

Basiert auf UDP

DHCP

  1. Client sucht DHCP Server mittels Broadcast
  2. DCHP Server antwortet (DHCP offer)
  3. Der Client wählt einen Server und fordert eine Auswahl der angebotenen Paramter (DHCP Request)
  4. Der Server bestätigt mit einer Message, welche die engültigen Parameter enthält
  5. Vor Ablauf der Lease-Time erneuert der Client die Adresse.

Transport Layer

User Datagramm Protocoll (UDP)

Daten werden an "Adresse" gesendet. Es wird auf kein ACK gewartet.

  • Verbindungslos
  • Unzuverlässig

Transmission Controll Protocoll

FeldBeschreibung
Sequence NumberNummer zur Ordnung der Segmente
Acknowledgment NumberBisherige ACK No + Anzahl Daten
Window SizeAnzahl Bytes die empfangen werden können

Verbindungsaufbau & Abbau

Fluss Steuerung

Stop and Wait

  • Verhindert den Datenüberlauf
  • Ineffiziente Nutzung der Netzbandbreite
  1. Paket senden
  2. ACK antworten

Sliding-Window

  • Höhere Durchsatzrate
  • Feste "Fenster-Grösse"
  1. X Pakete senden
  2. X ACK antworten

Network Layer / Internet Layer

Internet Protokoll Format (IP Header)

IP-Packet besteht aus einem Header (min. 20 Bytes) und Nutzdaten

Feld Beschreibung
VersionIPv4 / IPv6
IHLHeader Length in 4-Byte (20 Byte -> 5)
TOS (Type of Service)Erlaubt Priorisierung
Total Length Länge des IP-Packets (Header + Nutzdaten)
Identification Identifkiation des IP-Packets / Fragments
FlagsKontroll-Flags für Fragmenteriung
Fragment Offset Gibt an, wo ein Fragment hingehört (Position in buffer)
TTL (Time to Live) Hop Counter, 0 -> Paket wird verworfen
ProtocolÜbergeordnetes Protokoll

Internet Control Message Protocol (ICMP)

Maximum Transfer Unit (MTU)

  • Der Sender kennt die MTU der Netze nicht.
  • Die MTU gibt die maximale Paketgrösse (Layer 3) an.

Maximale Daten bei gegebener MTU

TCP

ARP

Data Link Layer

Framing

Asynchron

Keine Daten -> Nichts wird gesendet

Zu Beginn eines Frames wird ein Start-Bit gesendet

Synchron

  • Frames werden ohne Unterbruch gesendet

Fehlererkennung / Fehlerkorrektur

  • FER (Frame Error Ratio)
  • RER (Residual Error Ratio)
  • BER (Bit Error Ratio)

Parity

  • Even Parity: Wenn die Anzahl Bits, die 1 sind, gerade ist, dann ist der Wert: 1
  • Odd Parity: Wenn die Anzahl Bits, die 1 sind, ungerade ist, dann ist der Wert 1

Physical Layer (Bitübertragungsschicht)

Serielle asynchrone Übertragung (ohne Synchronisations-Takt)

Zwischen Sender und Empfänger werden folgende Abmachungen gemacht

  • Bitrate
  • Anzahl Datenbits (Typischerweise 1 Byte)
  • Anzahl Stopbits (Typischerweise 1 Bit)

Die Taktrückgewinnung ist möglich, solange regelmässig Zustandsänderungen auftreten.

Serielle synchrone Übertragung

Bei der synchronen Übertragung arbeitet der Empfänger mit dem gleichen Takt wie der Sender

  • Es werden keine Start- und Stopbits benötigt
  • Der Takt muss zusätzlich übertragen werden

Die Übertragung des Takts erfolgt über ein Codierungsverfahren oder eine zusätzliche Leitung.

Kommunikationsarten (Verkehrsbeziehung)

  • Simplex: Ein Kanal, in eine Richtung
  • Halbduplex: Ein Kanal, abwechslungsweise in zwei Richtungen
  • Vollduplex: Ein Kanal pro Richtung

Arten der Verbindungen (Kopplung)

  • Punkt-Punkt: Direkte Verbindung zweier Kommunikationspartner
  • Shared Medium: Mehrere Partner verwenden das gleiche Medium

Datenübertragungsrate

  • Baudrate: Symbole pro Sekunde
  • Zeichenrate: Zeichen pro Sekunde

Ethernet

Klasse bestimmen

KlasseAddressbereichNetzmaske
A0.0.0.0 - 127.255.255.255255.X.0.0
B128.0.0.0 - 191.255.255.255255.255.X.0
C192.0.0.0 - 223.255.255.255255.255.255.X

Subnetzmaske berechnen

Bits012345678
Dezimal1248163264128256

Es werden jeweils zwei Adressen für benötigt:

  • Broadcast (höchste Adresse)
  • Netzadresse (tiefste Adresse)
CIDR Subnet Maske Anzahl AdressenNutzbare Adressen
/32 255.255.255.25511
/31 255.255.255.2542 2 
/30255.255.255.25242
/29255.255.255.248 86
 /28255.255.255.2401614
/27 255.255.255.2243230
/26255.255.255.192 64 62
/25 255.255.255.128128 126
/24255.255.255.0  256 254
/23255.255.254.0  512 510
/22255.255.252.0 1024 1022
/21255.255.248.0  2048 2046
/20255.255.240.0  4096 4096
/19255.255.224.0  8192 8190
/18255.255.192.0  16,384 16,382
/17255.255.128.0  32768 32766

Anzahl Adressen:

Anzahl nutzbare Adressen:

### Subnetze aufteilen Die Subnetzmaske darf sich von der kleinsten zur grössten nicht ändern. Also von klein nach gross.

VLAN

Erlaubt es ein grosses Netz in unabhängige logische Netze aufzuteilen. Jeder Port am Switch kann einem beliebigen VLAN zugeordnet werden.

Spanning Tree Protokoll (STP)

  • Alle Knoten werden genau einmal verbunden
  • Verbindungen, die zu Schleifen führen werden deaktiviert

Bridge Identifier: Priorität + Mac Adresse

  1. Root bestimmen mittels Bridge-Identifier
  2. Direkt angeschlossene Bridges bestätigen
  3. Weitere Verbindungen abhängig von Kosten und Bridge-Identifier eintragen

Routing

Vom grössten Netz zum kleinsten.

Ethernet Systeme

Autonegotiation: Ermittlung der besten Betriebsart durch Austausch derLeistungsmerkmale zweier Netzwerkkomponenten

Link Pulses

  • NLP: Link Presence Detection
  • FLP: Autonegotiation, Autopolarity

Codierungen

Local Area Networks (LAN)

Beschreibung SymbolEinheit Formel
Framerate 
Bitrate 
FramelängeBitsData + Prä + SFD + FCS = Data + 7Bit + 1Bit + 4Byte
Nutzbitrate
Payload 
InterFrameGapIFGBytes12
 

Übertragungsarten

  • Unicast: an einzelne Stationen
  • Broadcast: an alle Stationen
  • Multicast: an eine Gruppe von Stationen

Leitungscode

Als Leitungscode wird ein Manchester eingesetzt

  • 1 positive Flanke, 1 negative Flanken
  • Erlaubt die Taktrückgewinnung auf einfache Weise
  • Benötigt die doppelte Bandbreite des theoretischen Minimums

Kollosionen

Können durch die Überlagerungen von Signalen entstehen. Kollosionen müssen erkannt werden.

Kollosionserkennung

Topologie

Beschreibung SymbolEinheit Formel
Nutzdaten  Bits
Länge zwischen Switch und Knoten 
Bandbreite
Framedauer s
 Transferdauer s 

Ethernet

IEEE Mac Adressen

Base64

  1. ASCII in binär umwandeln
  2. Binär in je 6 grosse Bitgruppen abstechen
  3. Wenn letzer Block kleiner als 6, mit 0 auffüllen
  4. Tabelle nachsehen und Char nehmen

WING

St.Galler Management Modell (SGMM)

Umweltsphären

Bezeichnen relevante Bezugsräume im Umfeld der Unternehmung.

Ökonomische (wirtschaftliche) Umwelt Rahmenbedingungen für ein Unternehmen (Entwicklung der Wirtschaft, Arbeitsmarkt, Teuerung, Wirtschaftsbeziehungen zum Ausland, etc.)

Technologische Umwelt Umfasst alles, was in den Berich Technik- und Naturwissensschaften fällt (Produktionsverfahren, Materialien, Transport- und Kommunikationsmittel, etc)

Soziale Umwelt Betrifft den Menschen mit seinen Wünschen und Vorstellungen (politische und gesellschaftliche Trendds, Wohlbefinden der einzelnen Menschen, etc)

Ökologische Umwelt Betrifft den Gesamthaushalt der Natur (Rohstoffe, Energie, Klima, Abfälle, etc)

Anspruchsgruppen / Stakeholder

Begriffskategorien

Ordnungsmomente

  • Stategie
  • Struktur
  • Kultur

Entwicklungsmodi

  • Optimierung
  • Erneuerung (schaffung von völlig neuem)

Prozesse

  • Managementprozess (Grundlegende Aufgaben, der Lenkung eines Unternehmens)
  • Geschäftsprozess (Leistungserstellung, Vertrieb)
  • Unterstützungsprozess (Personal, EDV, Finanzen)

Interaktionsthemen

Gegendstände der Austauschbeziehungen zwischen Anspruchsgruppen und der Unternehmung

  • Personen- und kulturgebundene Elemente
  • Objektgebunde Elemente (Resourcen)

Interaktionsthemenanalye

  1. Was beschreibt das Interaktionsthema, also den streitbaren Sachverhalt?
    1. Welche Resource (Produktionsfaktor) des Unternehmens ist betroffen?
    2. In welcher Umweltsphäre (Kontext) spielt sich der Sachverhalt ab?
  2. Welche Anspruchsgruppe ist betroffen?
    1. Welche Anliegen (veralgemeinerungsfähige Ziele) brint die Ansrpuchsgruppe vor?
    2. Welchce Interessen (unmittelbarer Eigennutzen) verfolgt die Anspruchsgruppe dabei?
    3. Welche Normen (Gesetze und Regeln) stützen das Anliegen der Anspruchsgruppen?
    4. Welche Werte (grundlegende Ansichten) stützen die Anliegen der Anspruchsgruppe?
  3. Was sind aus Unternehemssicht die...
    1. Gefahren, die sich aus der Situation für die betreffende Unternehmung ergeben?
    2. Reaktionsmöglichkeiten, die vom betreffenden Unternehmen ergriffen werden können?
Technische AusgabeWirtschaftliche Ausgabe
Die erforderliche Menge und Qualität des Materials ist zur richtigen Zeit am richtigen Ort bereitzustellen.Die Kosten der Materialbeschaffung sind zu optimieren

Bestandteile

Beschaffungslogistik

  • Bedarfsmittlung: Wie viel Material wird benötigt
  • Beschaffungsmarktforschung: Welche Preise gelten auf dem Markt? Welche Lieferanten drängen sich auf?

Lagerlostik

  • Lagerung: Wo sollen die Materialien gelagert werden?
  • Innenbetrieblicher Transport: Wie gelangen die Materialien zu den Verarbeitungsstellen?
  • Bestandsermittlung: Wie wird der aktuelle Lagerbestand ermittelt?

Produktionslogistik

  • Verbrauchsermittlung: Wie viel Material verbraucht die Fertigung?
  • Produktionsplanung: Wie kann der Produktionsprozess optimiert werden?

Absatzlogistik (extern)

  • Distribution: Wie wird der Vertrieb der Endprodukte organisiert?

Entsorgungslogistik

Entsorgung: Welche Materialien können wieder verwendet werden?

Beschaffungskonzepte

Vorratsbeschaffung (order to stock)

Die Beschaffungsmenge ist grööser als der aktuelle Beschaffungsbedarf. Dieses Konzept bietet sich bei erwarteten Preissteigerungen, bei vermutenden Lieferengpässen und bei unverderblichen Materialien an.

Fallweise Beschaffung (order to make)

Der Beschaffungsvorgang wird ausgelöst, wenn ein Materialbedarf festgestellt wird. Die Lagerhaltung wird damit an den Lieferanten übertragen. Dieses Konzept bietet sich nur dann an, wenn das Material jederzeit beschaffbar ist, der Materialbedarf nicht für längere Zeit im Voraus geplant werden kann und eine Vorratsbeschaffung aufgrund der hohen Kapitalbindung kostspielig wäre.

Just in Time (JIT)

Dieses Konzept passt die Beschaffung in zeitlicher und mengenmässiger Hinsicht genau dem Bedarf an. Dabei beginnt im Unterschied zur "Fallweisen Beschaffung" auch der Lieferant erst dann mit der Fertigung, wenn ein Kundenauftrag vorliegt. Eine Lagerhaltung erübrigt sich entsprechend sowohl beim Besteller als auch beim Lieferanten. Dieses Konzept bietet sich dann an, wenn auf eine pünktliche Lieferung des Lieferanten vertraut ewden kann. Kleinste Verspätungen würden zu einem Produktionsstopp führen.

Insourcing

Verlagerung von zuvor im Markt bezogenen Leistungen in die eigene Wertschöpfung.

Vorteile:

  • Reduktion von Lieferzeiten
  • Unabhängigkeit von Lieferanten, Preisen und Absatzmengen
  • Aufrechterhaltung von Qualitätsstandards
  • Auslastung der Fertigungskapazitäten

Outsourcing

Verlagerung von Teilen der Wertschöpfung auf externe Lieferanten (langfristig)

Vorteile

  • Minimierung der Fixkosten
  • Beschaffungsmenge der Zeitspanne flexibel planbar
  • Minimierung der Lagerkosten
  • Ausweichmöglichkeit bei Kapazitätsengpässen

Magisches Dreieck

Besteht aus:

  • Kapitalbindung und Lagerunterhalt
  • Beschaffungskosten
  • Lieferbereitschaft

Ziele:

  • Lieferbereitschaft
  • Beschaffunskosten
  • Kapitalbindung

Zielkonflikte:

  • Lieferbereitschaft vs. Kapitalbindung
  • Beschaffungskosten vs. Kapitalbindung

Analysen

ABC-Analyse

KategorieWertanteil am GesamtwertMengenanteil an der Gesamtmenge
A Güter 70% - 80% < 30%
B Güter15% - 20%30% - 50%
C Güter5% - 10%40% - 60%

XYZ-Analyse

KategorieBedarf / Vorhersagegenauigkeit Beispiel
X Güter Regelmässiger Bedarf/Vorhersagegenauigkeit hochGrundnahrungsmittel
Y GüterSchwankender Bedarf/ Vorhersagegenauigkeit begrenzt Glace
Z GüterUnregelmässiger Bedarf/Vorhersagegenauigkeit gering Medikamente

Lagerfunktionen

Zeitüberbrückung

Das Lager soll den Bedarf der Produktion an Materialien so schnell wie möglich bereitstellen und damit die Zeit bist zur Auslieferung überbrücken. Damit wird eine reibungslose Produktion sichergestellt.

Sicherung

Unternehmen unterliegen unvorhergesehenen Liefer- oder Bedarfsschwankungen. Arbeitet ein Lieferanten nicht fristgerecht, muss ein Unternehmen auf seine Sicherheitsbestände zurückgreifen können, um Produktionsausfälle zu vermeiden.

Spekulation

Auch Materialpreise unterliegen Preisschwankungen. Erwartet ein Unternehmen einen Preisanstieg (z.B. durch Verteuerung von Rohstoffen, Wechselkursschwankungen), kann ein Einkauf auf Vorrat trotz zusätzlicher Lagerkosten sinnvoll sein.

Veredelung bzw. Umformung

Durch die Lagerung entsteht eine gewollte Qualitätsverbesserung der Bestände. Gewisse halbfertige Produkte müssen beispielsweise zuerst auskühlen, bevor sie weiterverarbeitet werden können; andere durchlaufen vor der Auslieferung zuerst einen Reifeprozess, wie z.B. Käse. In diesem Fall ist die Lagerung Teil des Produktionsprozesses. Sie steigert den Wert des Produktes.

Assortierung

Bei Handelswaren dient die Lagerhaltung der Sortierung von Sammellieferungen oder teilweise auch der Warenpräsentation.

Kennzahlen

Durchschnittlicher Lagerbestand

Zählt zu den zentralen Kennzahlen der Lagerhaltung. Sie geben Auskunft darüber, wie hoch der Lagerbestand im Durchschnitt ist.

Lagerumschlagshäufigkeit

Diese Zahl gibt an, wie oft der durchschnittliche Lagerbestand in einem Jahr umgesetzt wird. Je niedriger diese Zahl, desto tiefer ist die Umschlagshäufigkeit. Je höher der Lagerumschlag, desto niedriger ist das im Lager gebundene Kapital.

Durschnittliche Lagerdauer

Diese Kennzahl gibt an, wie lange das Gut im Durchschnitt im Lager verbleibt. Je kürzer die Lagerung, desto geringer ist die Kapitalbindungsdauer. Die durchschnittliche Lagerdauer sinkt mit steigender Umschlagshäufigkeit ist.

Finanzbuchhaltung (FIBU)

Der laufende Geschäftsverkehr chronisch (im Journal) und systematisch (in den Konten) erfasst.

Die Finanzbuchhaltung dient

  • der Ermittlung der Vermögenslage mittels Inventar und Bilanz
  • der Feststellung des Erfolges mittels Erfolgsrechnung
  • Die Liquiditätssteuerung und Finanzplanung mittels Geldflussrechnung

Die Zahlen der Finanzbuchhaltung sind für die Führung der Unternehmung zum Teil ungeeignet, weil sie einerseits stile Reserven enthalten und anderseits nur den Gesamterfolg und nicht den Erfolg je Prdukt oder Dienstleistung ausweisen.

Betriebsbuchhaltung

In der Betriebsbuchhaltung werden die durch die Leistungserstellung entstandenen Kosten erfasst und den durch die Leistungsveräusserungen erzielten Erlösen gegenübergestellt.

Die Kostenerfassung erfolgt nach

  • Kostenarten (z.B. Materialkosten, Personalkosten, Abschreibungen)
  • Kostenstellen (z.B. nach verschiedenen Abteilungen wie Einkauf, Lager, Fabrkation, Verkauf)
  • Kostenträgern (z.B. nach den hergestellten Produkten oder Leistungen)

Die Betriebsbuchhaltung ist eine rein interne Rechnung. Sie stellt Unterlagen für die Kalkulation sowie die Kosten- und Erfolgskontrolle bereit.

Vollkostenrechnung

Verteilt die gesamten Kosten auf die verschiedenen Kostenträger

Kostenarten Kostenstellen Kostenträger
Welche Kosten fallen an?  Wo fallen Kosten an?Wofür fallen Kosten an?
z.B. Vollkornmehl, Hefe, Salz und Wasser, sowie Mietkosten für die Backstube, den Lagerraum und die Verkaufsläden z.B. in der Backstube, im Lagerraum und in den Verkaufslädenz.B. für Vollkornbrote oder Apfelkrapfen

-> Graphische Darstellungsform der Vollkostenrechnung ist der Betriebsabrechnungsbogen BAB

Kostenartenrechnung

Einzelkosten (direkte Kosten)

Erklärung: Alle während dem Leistungsprozess für ein bestimmtes Produkt (Kostenträger) entstandenen Kosten, welche sich diesem direkt zuordnen lassen. Beispiel: Die Materialkosten können jedem einzelnen Produkt genau zugeordnet werden. Ein Kleid braucht eine genau bestimmte Menge Stoff. Verwendung: Übertragung in die Kostenträgerrechnung

Gemeinkosten (indirekte Kosten)

Erklärung: Alle während dem Leistungsprozess entstandenen Kosten, welche sich nicht einem bestimmten Produkt zuordnen lassen, sondern zur Erstellung mehrerer Produkte angefallen sind. Es ist nur bekannt, in welchem Bereich sie entstanden sind. Beispiele: Die Mietkosten für das Fabrikationsgebäude lassen sich nicht direkt den einzelnen Produkten zuordnen, denn sie werden von allen erstellten Produkten gemeinsam verursacht. Verwendung: Übertragung in die Kostenstellenrechnung

Sachliche Abgrenzung

  • Kalkulatorische Zinsen (berücksichtigt, dass eigentlich auf dem Eigenkapital auch ein Zins bezahlt werden müsste -> Opportunitätskosten)
  • Kalkulatorischer Unternehmerlohn: fkitives Gehalt des Unternehmers
  • Kalukatorische Mieter: fiktive Miete für die Benutzung eigener Räumlichkeiten des Unternehmens -> Opportunitätskosten
  • Stille Reserven: Über-/Unterbewertung Anlagevermögen, Lager
  • Kalkulatorische Abschreibungen: Abweichender Abschreibungsaufwand

Kostenstellenrechnung

  • Verteilugn der Kostenarten (Gemeinkosten) auf Kostenstellen
  • Umlage von Kostenstellenkosten auf andere Kostenstellen (innerbetriebliche Leistungsverrechnung)
  • Ermittlung von Zuschlagssätzen für die Kostenträgerrechnung
  • Ermittlung von Soll-Ist-Abweichungen zur Wirtschaftlichkeitskontrolle

Kostenträgerrechnung

Die hergestellten Güter und die erbrachten Dienstleistungen werden als Kostenträger bezeichnet. Diese Kostenträger haben die von ihnen verursachten Kosten zu tragen.

Zuschlagssätze

Zuschlagsatz

GemeinKostenZuschlag (GKZ)

Mateiralgemeinkostenzuschlagsatz

Fertigungsgemeinkostenzuschlagsatz

Verwaltungskostenzuschlagsatz

Vertriebsgemeinkostenzuschlagsatz

Teilkostenrechnung

Umfasst alle Kostenrechnungssysteme, welche sich auf die Betrachtugn der direkt einem Kostenobjekt zurechenbaren Kosten (=Einzelkosten) beschränken.

Fixkosten: Diese Kosten fallen in konstanter Höhe an, unabhängig von produzierten Mengen eines Produkts, z.B. Mietkosten. Variable Kosten: Diese Kosten hängen von den produzierten Menge eines Produktes ab, z.B. Materialeinzelkosten Deckungsbeitrag: Dieser entsteht, wenn der Verkaufspreis grösser als die variablen Kosten pro Stück ist. Er steht zur Deckung der fixen Kosten zur Verfügung. Break-Even-Absatzmenge: Bei dieser Verkaufsmenge wir dei Gewinnschwelle erreicht.

Gesamtkosten Fixkosten + variable Kosten

Break-Even-Analyse (Gewinnschwelle / Nutzschwelle)

Mengenmässige Nutzschwelle berechnen

Deckungsbeitrag / Stück:  

Break-Even-Point berechnen

Kostenfunktion = Erlösfunktion Fixkosten (FK) + [var. Kosten (VK) /Stk * Menge(x)] = Preis(P)/Stk. * Menge(x)

Break-Even-Absatzmenge (x)

Bilanz

Zeigt Aktiv- und Passivbestände am Schluss bzwh. am Anfang einer Rechnungsperiode. Sie ist eine Momentanaufnahme, denn sie bezieht sich auf einen Zeitpunkt. Die Bilanz zeigt das Vermögen eines Unternehmens.

Funktionen

Dokumentation: Stellt eine BEstandsaufnahme der im Unternehmen vorhandenen Vermögen und Schulden an einem Stichtag dar Gewinnermittlung: In der Bilanz wird der Gewinn bzwh. der Verlust einer bestimmten Periode ersichtlich. Information: Informiert intern (als Steuerungsinstrument für das Unternehmen) sowie extern (z.B. Kapitalgeber, Staat, usw.) über die finanziellle Lage des Unternehmens.

Erfolgsrechnung

Sie zeigt die einer Rechnungs-periode, also in einem Zeitraum, entstandenen Aufwendungen und Erträge. Sie gibt dinene Einblick in das betriebliche Geschehen (Leistung) der Unternehmung und zeigt die Finanzierung des Vermögens.

Aktiven

Passiven

Erfolgsrechnung

Nettoverkaufspreis

+ Verkaufssonderkosten = Nettobarverkaufspreis + Skonto = Nettokreditverkafspreis + Rabatt = Bruttokreditverkaufspreis + MwST. = Bruttokreditverkaufspreis

Bruttokreditankaufspreis

Katalogpreis des Lieferanten

- Rabatt/-e = Nettokreditankaufspreis (= Rechnungsbetrag) - Skonto = Nettobareinkaufspreis + Bezugskosten = Einstandswert / Einstandspreis

Gesamt-Kalkulation

auf 5 Rappen gerundent (am Ende)

Strategie

Strategische Analyse

SWOT-Analyse

PESTEL-Analyse

5-Forces Modell von Porter

Unternehmensstrategie

Elemente

Identität

  • Wer sind wir?
  • Welchen generellen Sinn und/oder Zweck erfüllt unsere Existenz?

Ziele

  • Welchen wirtschaftlichen Zweck verfolgen wir?
  • Welche Produkte/Dienstleistungen stellen wir her?

Verhaltensgrundsätze

  • Wie verhalten wir uns gegenüber Anspruchsgruppen?
  • Welche Grundsätze gelten für unser tägliches Handeln?
ElementeFrageErläuterung
DifferenzierungWie können wir gewinnen Kunden für uns gewinnen / Von Konkurrenz abheben
Wirtschaftlichkeit Wie gelangen wir dzu unseren Einnahmen?Tiefe Produktionskosten / Hohe Verkaufspreise

Strategische Entscheidungen

Müssen zur Kultur, der Organisation, den Werten und zum Leitbild passen!

Strategisches Planen

Absatzmärkte

B - to - B

Busniess to Business-Marketing (Fachkundengeschäft)

  • Vermarktung von einem Unternehmen zu einem andern Unternehmen (z.B. Produktionsmaschinen)

B - to - C

Business to Consumer Marketing (Privatkundengeschäft)

  • Vermarktugn von einem Unternehmen zu einem Privathaushalt (z.B. Konsumgüter)

C - to - C

Customer-to-Customer Marketing

  • Vermarktung von einem Privathaushalt zu einem anderen Privathaushalt (z.B. Privatverkauf über eBay)

Marktforschung

Quantitativ (Marktdaten)

Ziel: Zahlenwerte über den Markt ermitteln

  • Wie gross ist das Marktvolumen?
  • Wie gross ist der Marktanteil eines Unternehmens?

Qualitativ (Kundenbenbedürfnisse)

Motive für Verhaltenswiesen der Konsumenten herausfinden

  • Welche Erwartungen haben Kunden von bestimmten Produkten?
  • Warum kauft ein Kunde ein bestimmtes Produkt?

Erhebungsmethoden

Primärmarktforschung

Ermittlung neuer, bisher noch nicht erfasster Daten

  • Befragung (mündlich oder schriftlich mittels Fragebogen)
  • Beobachtungen (können verdeckt oder offen durchgefüht werden)
  • Experimente, z.B. Wirkungsweisen von Produkten
  • Kundenkaretn

Sekundärmarktforschung

Die Auswertungen basieren auf bereits vorhandenen Daten, die ursprünglich für andere Zwecke erfasst wurden.

  • innerbetriebliche Quellen wie Absatzstatistiken, Reparaturlisten oder Kundenreklamationen
  • externe Quellen, z.B. Forschungsergebnisse, veröffentlichte Statistiken, Geschäftsberichte anderer Unternehmen

Marktvolumen: Tatsächlicher Absatz bzwh. Umsatz aller Anbieter auf dem Markt Marktpotenzial: Anzahl möglicher Kundne, bzwh. Menge wenn alle Kunden kaufbereit sind (beim gegebenen Preis) Marktkapazität: Gesamtanzahl möglicher Kunden, bzwhl. max. Menge an Produkten, die gebraucht /verbraucht werden können (wenn Preis kene Rolle spielt) Marktanteil: Abgesetze Menge, bzwh. Umsatz eines Anbieters / ihres Unternehmens im Verhältnis zum gesamten Marktvolume

Marketing 4-P-Mix

Gibt vor ob man sich als Unternehmen gesamthaft auf möglichst höhe Qualität, oder möglichst tiefe Preise fokussiert

Prduct (Produkt)

  • Absatzsprogramm / Sortimen
  • Produkteigenschaften
  • Verpackung
  • Serviceleistungen
  • Garantieleistungen

Price (Preis)

  • Preisbestimmung
  • Preisstrategie
  • Konditionen

Place (Distribution)

  • Absatzwege
  • Transportmittel

Promotion (Kommunikation)

  • Werbung
  • Öffentlichkeitsarbeit
  • Sponsoring

HM2

Ableitung in Python

from sympy import *
import numpy as np

x = Symbol('x')
y = x**2 + 1

yprime = y.diff(x)
print(yprime)

Tangentengleichung

Verallgemeinerter Tangentengleichung

Es gilt:  

Spezialfall  

Python

import sympy as sp

x0_values = [1, 0 ,1]

# Variablen definieren
x1, x2, x3 = sp.symbols('x1, x2, x3')

# Aus einzelnen Variablen eine Matrix machen
x = sp.Matrix([x1, x2, x3])

# Gegebene Werte befüllen
x0 = sp.Matrix(x0_values)

# Skalare Terme erzeugen
f1 = x1 * sp.cos(x2) + x3
f2 = x1**2 + sp.sin(x2)*x3**3

# Aus Termen eine Matrix machen
f = sp.Matrix([f1, f2])

# Terme zahlenmässig auswerten
fx0 = f.subs([
    (x1, x0_values[0]),
    (x2, x0_values[1]),
    (x3, x0_values[2])
])

# Jacobi Matrix bilden
Df = f.jacobian(x)

# Jacobi Matrix zahlenmässig auswerten
Dfx0 = Df.subs([
    (x1, x0_values[0]),
    (x2, x0_values[1]),
    (x3, x0_values[2])
])

# Linearisierung von f an Stelle x0 bilden
g = fx0 + Dfx0@(x- x0)

# Skalare Komponenten der Linearisierung herausnehmen
g1 = g[0, 0]
g1_value = g1.subs([
    (x1, x0_values[0]),
    (x2, x0_values[1]),
    (x3, x0_values[2])
])

g2 = g[1, 0]
g2_value = g2.subs([
    (x1, x0_values[0]),
    (x2, x0_values[1]),
    (x3, x0_values[2])
])

print ("Das Resultat lautet (Matrixförmig)")
print (g1)
print (g2)

Mehrdimensionales Newtonverfahren

$$ f: \mathbb{R} \to \mathbb{R}^2 $ $$

Python

import sympy as sp

# Variablen in Funktion definieren
x1, x2 = sp.symbols('x1, x2')

# Funktionen definieren
f1 = x1**2 + x2 - 11
f2 = x1 + x2**2 - 7

f = sp.Matrix([
    [f1],
    [f2]
])

def f_at(x):
    
    return f.subs([
        (x1, x[0]),
        (x2, x[1])
    ])

# x0 definieren
x0 = sp.Matrix([
    [1.],
    [1.]
])

# Partielle Ableitungsmatrix erstellen
Df = sp.Matrix([
    [sp.diff(f1, x1), sp.diff(f1, x2)],
    [sp.diff(f2, x1), sp.diff(f2, x2)]
])

print ("Df = \n", Df)

#x0 in Df einsetzen
def Df_at(x):
    Df_at = Df.subs([
        (x1, x[0, 0]),
        (x2, x[1, 0])
    ])
    
    return Df_at


# Newtonverfahren
x = [ x0 ]

# Nullstelle von f
xstar = sp.Matrix([
    [3],
    [2]
])

# Anzahl Iterationsschritte
print ("Newtonverfahren")
for i in range(1, 5):
    x_prev = x[i-1]
    Df_prev = Df_at(x_prev)
    
    Df_inv = Df_prev.inv()
    
    f_at_xi = f_at(x_prev)
    xi = x_prev - Df_inv @ f_at_xi
    x.append(xi)
    

    print ('----------------------------------------')
    
    print (f"i = {i}")
    print ("xi = \n", xi)
    print (f"Fehler ||xi - xstar || = {(xi - xstar).norm(2)}")

Newtonverfahren mit Dämpfung

import numpy as np
import sympy as sp

x1, x2 = sp.symbols('x1, x2')
x = sp.Matrix([x1, x2])

f1 = x1**2 + x2 - 11
f2 = x1 + x2**2 - 7

f = sp.Matrix([f1, f2])

Df = f.jacobian(x)

f = sp.lambdify([ [[x1],[x2]] ], f, 'numpy')
Df = sp.lambdify([ [[x1],[x2]] ], Df, 'numpy')

x0 = np.array([[1.],[1.]])
print ('x0 =', x0)
print ('|| f(x0) || = ', np.linalg.norm(f(x0), 2))

imax = 5
for i in range(1, imax+1):
    delta0 = np.linalg.solve(Df(x0), -f(x0))
    
    kmax = 4
    k = 0
    while (k <= kmax) and (np.linalg.norm(f(x0 + delta0/2**k),2) >=
                          np.linalg.norm(f(x0), 2)):
        k = k +1 
    
    if k == kmax + 1:
        k = 0
        
    x1 = x0 + delta0 / 2**k
    
    print ('------------------------------------')
    print ('Iteration i =', i)
    print ('Dämpfung k =', k)
    print ('xi =', x1)
    
    print ('|| f(xi) || =', np.linalg.norm(f(x1), 2))
    
    x0 = x1

Jacobi Matrix

# Manueller Weg
Df = sp.Matrix([
    [sp.diff(f1, x1), sp.diff(f1, x2)],
    [sp.diff(f2, x1), sp.diff(f2, x2)]
])
f = sp.Matrix([f1, f2])
x = sp.Matrix([x1, x2])

Df = f.jacobian(x
			   )

Skalarwertige Funktionen mit mehreren Variablen

  • Eine Funktion mit  $$ unabhängigen Variablen  $1, …, x) und einer abhängigen Variablen  $$ versteht man eine Vorschrift, die jedem geordneten Zahlentupel ( $1, x_2, …, x)) aus einer Definitionsmenge  $ \in \mathbb{R}^) genau ein Element  $$ aus einer Wertemenge  $mathbb{W} \in \mathbb{R}$ zuordnet. Symbolische Schreibweise:

$$ f: \mathbb{D} \in \mathbb{R}^n \rightarrow \mathbb{W} \mathbb{R} (x_1,x_2,…,x_n) \mapsto y = f(x_1, x_2, …, x_n) $$

  • Da das Ergebnis  $ \in \mathbb{R}$ ein Skalar (eine Zahl) ist, redet man von einer skalarwertigen Funktion.

Partielle Ableitung

$$ z = f(x,y) = x^5 + x^7 $$

Beispiel

Gegen ist die folgende Funktion:  $(x,y) = x^5 + y^). Soll diese nach x abgeleitet werden, dann entsteht nachstehendes. Da y als Zahl angesehen wird, fällt dieses weg. $$ \frac{ \partial f(x,y) }{ \partial x }

5x^4 $$

Definition

Für eine Funktion  $: \mathbb{R} \rightarrow \mathbb{R}$ mit einer Variablen ist die Ableitung an der Stelle  $) bekanntlich definiert als: $$ f’(x_0) = \lim\limits{\Delta x \rightarrow 0} \frac{ f(x_0 + \Delta x) - f(x_0) }{ \Delta x } $$ Aus geometrischer Sicht entspricht dies der Steigung  $ = f‘(x_0)$ der im Punkt $(x_0, f(x_0))$ angelegten Kurventangente $$ t(x) = f(x_0) + f‘(x_0)(x- x_0) $$

Python

from sympy import symbols, diff

# Variablen in Funktion definieren
x, y = symbols('x y', real=True)

# Funktion definieren
f = x**5 + x**7

print(diff(f, x))

An einer Stelle

$$ \frac{\partial f}{\partial x} (x, y)

x^2 - x\cdot y^2 + \frac{1}{2} \cdot y^3 + 10 $$

from sympy import symbols, diff

# Variablen in Funktion definieren
x, y = symbols('x y', real=True)

# Funktion definieren
f = x**2 -x * y**2 + 1/2. * y**3 + 10

# Partielle ableitungen berechnen
df_x = diff(f, x)
df_y = diff(f, y)

# An einer Stelle (-1, 1)
print ("Partielle Ableitung nach x:", df_x)
print ("Partielle Ableitung nach y:", df_y)

x0 = -1
y0 = 1
df_x_eval = df_x.subs([
    (x, x0),
    (y, y0)
])
    
df_y_eval = df_y.subs([
   (x, x0),
   (y, y0)
])

print(f"An Stelle x={x0}, y={y0} = ({df_x_eval}, {df_y_eval})")

Interpolation

Die Interpolation ist ein Speziialfall der linearen Ausgleichsrechnung, bei dem wir zu einer Menge von vorgegebenen Punkten eine Funktion suchen, die exakt (und nicht nur näherungsweise) durch diese Punkte verläuft.

Polynominterpolation

#Sample data points
x = [1, 2, 3, 4, 5]
y = [2, 1, 3, 2, 4]

#Polynomial interpolation
n = len(x)
coefficients = []

for k in range(n):
    numerator = 1
    denominator = 1

    for j in range(n):
        if j != k:
            numerator *= (x[k] - x[j])
            denominator *= (x[k] - x[j])

    coefficient = y[k] * (numerator / denominator)
    coefficients.append(coefficient)

#Evaluate the polynomial at a specific point
x_new = 2.5
y_new = sum(coefficients[i] * x_new**(n-1-i) for i in range(n))

print("Interpolated value at x =", x_new, "is y =", y_new)

Lagrange Interpolation

x = [8.00, 10.00, 12.00, 14.00]
y = [11.2, 13.4, 15.3, 19.5]

xp = 11

m = len(x)

n = m -1
yp = 0

for i in range(n+1):
    p = 1
    for j in range(n+1):
        if j != i:
            p *= (xp - x[j])/(x[i] - x[j])
    yp += y[i] * p
    
print (f"Y Wert für x= {xp}: {yp}")

Splineinterpolation

# Define the original data points
x = [1, 2, 3, 4, 5]
y = [2, 4, 1, 5, 3]

# Define the points where you want to estimate values
x_new = [1.5, 3.5]

# Perform spline interpolation
y_new = []
for xi in x_new:
    # Find the interval where xi lies
    for i in range(len(x) - 1):
        if x[i] <= xi <= x[i + 1]:
            h = x[i + 1] - x[i]
            t = (xi - x[i]) / h

            # Calculate the estimated y-value using cubic spline interpolation formula
            a = (-t**3 + 2 * t**2 - t) * y[i]
            b = (3 * t**3 - 5 * t**2 + 2) * y[i + 1]
            c = (-3 * t**3 + 4 * t**2 + t) * (y[i + 1] - y[i]) / h
            d = (t**3 - t**2) * (y[i + 1] - y[i]) / h

            y_new.append(a + b + c + d)
            break

print(y_new)

Ausgleichsrechnung

Im Gegensatz zur Interpolation versuchen wir bei der Ausgleichsrechnung nicht, eine Funktion zu finden, die exakt durch sämtliche Datenpunkte geht, sondern diese nur möglichst gut approximiert.

  • Gegeben sind Wertepaare (, ) i=1, ..., n mit für .
  • Gesucht ist eine stetige Funktion , die die Wertepaare in einem gewissen Sinn bestmöglich annähert, d.h. dass möglichst genau gilt:

für alle

Ansatzfunktionen

  • Gegeben sei eine Menge von stetzigen Ansatzfunktionen auf dem Intervall sowie Wertepaare () für .

  • Ein Element heisst Ausgleichsfunktion von zu den gegebenen Wertepaaren, falls das Fehlerfunktional

für minimal wird, d.h. .

  • Man nennt das gefundenen dann optimal im Sinne der kleinsten Fehlerquadrate (least squares fit).

Lineare Ausgleichsprobleme

# Sample data points
x = [1, 2, 3, 4, 5]
y = [2, 1, 3, 2, 4]

# Calculate the coefficients (slope and intercept) of the best-fit line
n = len(x)
sum_x = sum(x)
sum_y = sum(y)
sum_xx = sum(x_i**2 for x_i in x)
sum_xy = sum(x[i] * y[i] for i in range(n))

slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x**2)
intercept = (sum_y - slope * sum_x) / n

# Evaluate the best-fit line at a specific point
x_new = 2.5
y_new = slope * x_new + intercept

print("Interpolated value at x =", x_new, "is y =", y_new)

Gauss Newton mit QR ohne Dämfpung


import numpy as np

def gauss_newton_no_damping(J, r, x0, max_iter=100, tol=1e-6):
    x = x0
    for i in range(max_iter):
        Jx = J(x)
        rx = r(x)
        Q, R = np.linalg.qr(Jx)
        delta = np.linalg.lstsq(R, -rx, rcond=None)[0]
        x += delta
        if np.linalg.norm(delta) < tol:
            break
    return x

# Beispielanwendung
def J(x):
    # Jacobimatrix
    n = len(observed_x_values)
    Jx = np.zeros((n, 3))  # 3 ist die Anzahl der Parameter (a, b, c)
    Jx[:, 0] = observed_x_values ** 2  # Ableitung nach dem Parameter a
    Jx[:, 1] = observed_x_values  # Ableitung nach dem Parameter b
    Jx[:, 2] = 1  # Ableitung nach dem Parameter c
    return Jx

def r(x):
    # Residuenvektor
    a, b, c = x
    return a * observed_x_values ** 2 + b * observed_x_values + c - observed_y_values

observed_x_values = np.array([0., 1., 2.])
observed_y_values = np.array([1., 2., 8.])

x0 = np.array([1.0, 1.0, 1.0])  # Anfangswert für die Parameter a, b, c
result = gauss_newton_no_damping(J, r, x0)

print("Ergebnis:", result)

Numerische Integration

Im Gegensatzu zur Ableitung, die für alle differenzierbaren Funktionen analytisch berechnet werden kann, können Integrale für eine Vielzahl von Funktionen nicht analytisch gelöst werden. Verfahren zur numerischen Integration (man spricht auch von Quadratur) spielen daher eine wichtige Rolle.

Rechteckregel

def rectangle_rule(f, a, b, n):
    """
    Approximates the integral of a function using the rectangle rule.

    Args:
    f: The function to integrate.
    a: The lower bound of the interval.
    b: The upper bound of the interval.
    n: The number of subdivisions (rectangles) for the approximation.

    Returns:
    The approximate value of the integral.
    """
    width = (b - a) / n
    integral = 0.0

    for i in range(n):
        x = a + i * width
        integral += f(x)

    integral *= width
    return integral

# Example usage
def f(x):
    return x**2  # Function to integrate

a = 0  # Lower bound
b = 1  # Upper bound
n = 100  # Number of subdivisions

approximation = rectangle_rule(f, a, b, n)
print("Approximated integral:", approximation)

Summierte Rechteckregel

import numpy as np

# Implementation
def sum_reckteck(f, a, b , h):
    n = int((b-a)/h)
    R = 0
    for i in range(0, n):
        x = a + i *h
        R = R + f(x+h/2)
    R = h*R
    
    return R

# Beispiel
def f(x):
    return 1./x

a = 2.
b = 4.
h = 0.5

R = sum_reckteck(f, a, b, h)
print ('R =', R)

Trapezregel

def trapezoidal_rule(f, a, b, n):
    """
    Approximates the integral of a function using the trapezoidal rule.

    Args:
    f: The function to integrate.
    a: The lower bound of the interval.
    b: The upper bound of the interval.
    n: The number of subdivisions (trapezoids) for the approximation.

    Returns:
    The approximate value of the integral.
    """
    width = (b - a) / n
    integral = 0.0

    for i in range(n):
        x0 = a + i * width
        x1 = a + (i + 1) * width
        integral += (f(x0) + f(x1)) / 2

    integral *= width
    return integral

# Example usage
def f(x):
    return x**2  # Function to integrate

a = 0  # Lower bound
b = 1  # Upper bound
n = 100  # Number of subdivisions

approximation = trapezoidal_rule(f, a, b, n)
print("Approximated integral:", approximation)

Summierte Trapezregel

import numpy as np

# Implementation
def sum_trapez(f, a, b , h):
    n = int((b-a)/h)
    T = (f(a) + f(b))/2
    for i in range(1, n):
        x = a + i *h
        T = T + f(x)
    T = h*T
    
    return T

# Beispiel
def f(x):
    return 1./x

a = 2.
b = 4.
h = 0.5

T = sum_trapez(f, a, b, h)
print ('T =', T)

Simpson-Regel

def simpson_rule(f, a, b, n):
    """
    Approximates the integral of a function using Simpson's rule.

    Args:
    f: The function to integrate.
    a: The lower bound of the interval.
    b: The upper bound of the interval.
    n: The number of subdivisions for the approximation (must be even).

    Returns:
    The approximate value of the integral.
    """
    h = (b - a) / n
    integral = 0.0

    for i in range(n):
        x0 = a + i * h
        x1 = x0 + h / 2
        x2 = x0 + h

        integral += (f(x0) + 4 * f(x1) + f(x2))

    integral *= h / 6
    return integral

# Beispielverwendung
def f(x):
    return x**2  # Funktion, die integriert werden soll

a = 0  # Untere Grenze
b = 1  # Obere Grenze
n = 100  # Anzahl der Unterteilungen (muss gerade sein)

approximation = simpson_rule(f, a, b, n)
print("Approximiertes Integral:", approximation)

Summierte Sympsonregel

def composite_simpson_rule(f, a, b, n):
    """
    Approximates the integral of a function using the composite Simpson's rule.

    Args:
    f: The function to integrate.
    a: The lower bound of the interval.
    b: The upper bound of the interval.
    n: The number of subdivisions for the approximation (must be even).

    Returns:
    The approximate value of the integral.
    """
    h = (b - a) / n
    x = [a + i * h for i in range(n + 1)]
    integral = 0.0

    for i in range(0, n, 2):
        integral += (f(x[i]) + 4 * f(x[i + 1]) + f(x[i + 2]))

    integral *= h / 3
    return integral

# Beispielverwendung
def f(x):
    return x**2  # Funktion, die integriert werden soll

a = 0  # Untere Grenze
b = 1  # Obere Grenze
n = 100  # Anzahl der Unterteilungen (muss gerade sein)

approximation = composite_simpson_rule(f, a, b, n)
print("Approximiertes Integral:", approximation)

Fehler der Sympsonregel


def composite_simpson_error(f, a, b, n):
    """
    Estimates the error of the composite Simpson's rule.

    Args:
    f: The function to integrate.
    a: The lower bound of the interval.
    b: The upper bound of the interval.
    n: The number of subdivisions for the approximation (must be even).

    Returns:
    The estimated error of the integral approximation.
    """
    h = (b - a) / n
    fourth_derivative = max(abs(fourth_derivative_func(f, x)) for x in numpy.linspace(a, b, 1000))
    error = -((b - a) * h**4 / 180) * fourth_derivative
    return error

def fourth_derivative_func(f, x):
    """
    Computes the fourth derivative of a function at a given point using finite differences.

    Args:
    f: The function.
    x: The point to evaluate the fourth derivative.

    Returns:
    The fourth derivative of the function at the given point.
    """
    h = 1e-5
    return (f(x - 2*h) - 4*f(x - h) + 6*f(x) - 4*f(x + h) + f(x + 2*h)) / h**4

## Romberg-Extrapolation

def romberg_integration(f, a, b, n):
    """
    Approximates the integral of a function using Romberg integration.

    Args:
    f: The function to integrate.
    a: The lower bound of the interval.
    b: The upper bound of the interval.
    n: The number of iterations for the extrapolation.

    Returns:
    The approximate value of the integral.
    """
    r = [[0] * (n + 1) for _ in range(n + 1)]
    h = b - a

    r[0][0] = (f(a) + f(b)) * h / 2

    for i in range(1, n + 1):
        h /= 2
        sum_term = 0

        for j in range(1, 2**i, 2):
            sum_term += f(a + j * h)

        r[i][0] = r[i-1][0] / 2 + sum_term * h

        for k in range(1, i + 1):
            r[i][k] = r[i][k-1] + (r[i][k-1] - r[i-1][k-1]) / (4**k - 1)

    return r[n][n]

# Beispielverwendung
def f(x):
    return x**2  # Funktion, die integriert werden soll

a = 0  # Untere Grenze
b = 1  # Obere Grenze
n = 5  # Anzahl der Iterationen

approximation = romberg_integration(f, a, b, n)
print("Approximiertes Integral:", approximation)

Differentialgleichung

Transformation in DGL System 1. Ordnung

  1. Die Differentialgleichung nach der höchsten vorkommenden Ableitung der unbekannten Funktion auflösen.
  2. Neue Hilfsfunktionen für die unbekannte Funktion und deren Ableitungen bis Ordnung der höchsten Ableitung minus 1 einführen.
  3. Das System erster Ordnung durch Ersetzen der höheren Ableitung durch die Funktionen aufstellen.
  4. Das entsprechende Anfangswertproblem in vektorieller Form aufschreiben.

Aus Ableitungen die Funktion nehmen

Ersatzwerte für Funktionen erstellen

Ersatzwerte einsetzen

Gleichungssystem aufstellen

Wenn Anfangswert gegeben:

mit

Gewöhnliche Differentialgleichung

In vielen Prozessen in der Natur oder Technik spielen zeitliche Änderungen von beobachtbaren Grössen ('Observablen'), nennen wir z.B. eine wichtige Rolle. Häufig ist die zeitliche Änderung einer solchen Grösse, also , abhängig von der Grösse selbst, d.h. wir können durch eine Funktion ausdrücken, die von der Zeit aber eben auch von der eigentlichen Grösse abhängt:

Richtungsfeld

import numpy as np
import matplotlib.pyplot as plt

def dy_dx(x, y):
    return x + y

x = np.linspace(-5, 5, 20)  # x-Werte im Bereich von -5 bis 5
y = np.linspace(-5, 5, 20)  # y-Werte im Bereich von -5 bis 5

X, Y = np.meshgrid(x, y)
U = 1  # x-Komponente der Vektoren (konstant 1)
V = dy_dx(X, Y)  # y-Komponente der Vektoren basierend auf der Differentialgleichung

plt.quiver(X, Y, U, V)  # Richtungsfeld zeichnen
plt.xlabel('x')
plt.ylabel('y')
plt.title('Richtungsfeld der Differentialgleichung dy/dx = x + y')
plt.grid(True)
plt.show()

## Euler Verfahren


import numpy as np
import matplotlib.pyplot as plt

def dy_dx(x, y):
    return x + y

def euler_method(f, x0, y0, h, num_steps):
    x = np.zeros(num_steps + 1)
    y = np.zeros(num_steps + 1)
    x[0] = x0
    y[0] = y0

    for i in range(num_steps):
        y[i+1] = y[i] + h * f(x[i], y[i])
        x[i+1] = x[i] + h

    return x, y

x = np.linspace(-5, 5, 20)  # x-Werte im Bereich von -5 bis 5
y = np.linspace(-5, 5, 20)  # y-Werte im Bereich von -5 bis 5

X, Y = np.meshgrid(x, y)
U = 1  # x-Komponente der Vektoren (konstant 1)
V = dy_dx(X, Y)  # y-Komponente der Vektoren basierend auf der Differentialgleichung

plt.quiver(X, Y, U, V)  # Richtungsfeld zeichnen

# Lösungskurve berechnen
x0 = 0  # Anfangswert für x
y0 = 3  # Anfangswert für y
h = 0.1  # Schrittweite
num_steps = 100  # Anzahl der Schritte

x_sol, y_sol = euler_method(dy_dx, x0, y0, h, num_steps)
plt.plot(x_sol, y_sol, 'r', label='Lösungskurve für y(0) = 3')

plt.xlabel('x')
plt.ylabel('y')
plt.title('Richtungsfeld und Lösungskurve der Differentialgleichung dy/dx = x + y')
plt.grid(True)
plt.legend()
plt.show()

Runge Kutta Verfahren


def runge_kutta(f, x0, y0, h, num_steps):
    x = [x0]
    y = [y0]

    for i in range(num_steps):
        k1 = h * f(x[i], y[i])
        k2 = h * f(x[i] + h/2, y[i] + k1/2)
        k3 = h * f(x[i] + h/2, y[i] + k2/2)
        k4 = h * f(x[i] + h, y[i] + k3)

        x.append(x[i] + h)
        y.append(y[i] + (k1 + 2*k2 + 2*k3 + k4) / 6)

    return x, y

def dy_dx(x, y):
    return x + y

x0 = 0  # Anfangswert für x
y0 = 3  # Anfangswert für y
h = 0.1  # Schrittweite
num_steps = 100  # Anzahl der Schritte

x_sol, y_sol = runge_kutta(dy_dx, x0, y0, h, num_steps)

plt.plot(x_sol, y_sol, 'r', label='Lösungskurve für y(0) = 3')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Lösungskurve der Differentialgleichung dy/dx = x + y mit dem Runge-Kutta-Verfahren')
plt.grid(True)
plt.legend()
plt.show()

Runge Kutta in Vektorieller Form


import numpy as np
import matplotlib.pyplot as plt


def rk4_vektor(f, a, b, y0, h):
    x = np.arange(a, b + h, h)
    y = np.zeros((y0.size, x.size))
    n = x.size
    # AB
    y[:, 0] = y0

    for i in range(0, n-1):
        k1 = f(x[i], y[:, i])
        k2 = f(x[i] + h / 2, y[:, i] + h / 2 * k1)
        k3 = f(x[i] + h / 2, y[:, i] + h / 2 * k2)
        k4 = f(x[i] + h, y[:, i] + h * k3)
        y[:, i + 1] = y[:, i] + h/6 * (k1 + 2*k2 + 2*k3 + k4)

    return x, y


def fh(t, z):
    u = (300000.0 - 80000.0) / 190.0
    vrel = 2600.0
    ma = 300000.0
    g = 9.81
    z1 = z[0]
    z2 = z[1]
    return np.array([z2, vrel*(u/(ma - u * t)) - g - (np.exp(-z1/8000.0)/(ma - u * t)) * z2**2])


# Tstart
a = 0.

# Tende
b = 190.0

h = 0.1

# h(0) = h'(0) = 0
y0 = np.array([0.0, 0.0])

# z[0] = z1 = h(t); z[1] = z2 = v(t)
x, z = rk4_vektor(fh, a, b, y0, h)


def z3(t, z):
    u = (300000.0 - 80000.0) / 190.0
    vrel = 2600.0
    ma = 300000.0
    g = 9.81
    z1 = z[0]
    z2 = z[1]
    return vrel*(u/(ma - u * t)) - g - (np.exp(-z1/8000.0)/(ma - u * t)) * z2**2


plt.plot(x, z[0, :], label="h(t)")
plt.title("h(t)")
plt.grid()
plt.figure()
plt.plot(x, z[1, :], label="v(t)")
plt.title("v(t)")
plt.grid()
plt.figure()
plt.plot(x, z3(x, z), label="a(t)")
plt.title("a(t)")
plt.grid()

plt.show()

Stabilitätsfunktion

import numpy as np
import matplotlib.pyplot as plt

def stability_function(z):
    return 1 + z + 0.5*z**2 + (1/6)*z**3 + (1/24)*z**4

z = np.linspace(-5, 5, 100)  # Werte für z im Bereich von -5 bis 5
S = stability_function(z)

plt.plot(z, np.abs(S), label='|Stabilitätsfunktion|')
plt.xlabel('Re(z)')
plt.ylabel('|Stabilitätsfunktion|')
plt.title('Stabilitätsfunktion des Runge-Kutta-Verfahrens (RK4)')
plt.grid(True)
plt.legend()
plt.show()

Mittelpunktverfahren

Mit Anzahl Iterationen

def mittelpunktverfahren(f, a, b, n):
    h = (b - a) / n  # Berechne die Schrittweite
    result = 0.0
    
    for i in range(n):
        x = a + (i + 0.5) * h  # Berechne den Mittelpunkt des aktuellen Intervalls
        result += f(x)  # Addiere den Funktionswert am Mittelpunkt zum Ergebnis
    
    result *= h  # Multipliziere mit der Schrittweite, um das Integral zu approximieren
    return result


def f(x):
    return x**2

integral_approximation = mittelpunktverfahren(f, 0, 1, 100)
print(integral_approximation)

Mit Schrittweite

def mittelpunktverfahren(f, a, b, h):
    n = int((b - a) / h)  # Berechne die Anzahl der Teilintervalle
    result = 0.0
    
    for i in range(n):
        x = a + (i + 0.5) * h  # Berechne den Mittelpunkt des aktuellen Intervalls
        result += f(x)  # Addiere den Funktionswert am Mittelpunkt zum Ergebnis
    
    result *= h  # Multipliziere mit der Schrittweite, um das Integral zu approximieren
    return result

def f(x):
    return x**2

integral_approximation = mittelpunktverfahren(f, 0, 1, 0.1)
print(integral_approximation)

Person:

  • polynominterpolation in python

ChatGPT:

  • Polynomial interpolation in Python can be done using the NumPy library. Here's a simple example:

In this example, we have x and y arrays representing the sample data points. The np.polyfit function fits a polynomial of degree len(x)-1 to the data, and the resulting coefficients are stored in the coefficients array. The np.poly1d function creates a polynomial object using these coefficients.

You can then evaluate the interpolated polynomial at a specific point by calling the polynomial object with that point. In this case, we calculate y_new at x_new = 2.5.

Note that the degree of the polynomial is set to len(x)-1 to ensure that the polynomial passes through all the given points.

Person:

  • ohne numpy

ChatGPT:

  • If you don't want to use NumPy, you can still perform polynomial interpolation in Python using the polyfit function from the numpy.polynomial module. Here's an example:

In this code, we import the Polynomial class from numpy.polynomial module. The Polynomial.fit function performs polynomial interpolation on the given data points. The deg parameter specifies the degree of the polynomial to fit, which is set to len(x)-1 to ensure interpolation through all the data points.

Once the polynomial is obtained, you can evaluate it at a specific point by calling the polynomial object with that value. In this example, we calculate y_new at x_new = 2.5.

Please note that although the NumPy library is not directly used, the numpy.polynomial module is still imported for the Polynomial.fit function.

Person:

  • ganz ohne python

ChatGPT:

  • Ohne die Verwendung von Python ist es schwierig, polynomiale Interpolation durchzuführen, da dies eine Programmiersprache ist, die speziell für solche Aufgaben entwickelt wurde. Wenn Sie jedoch eine andere Methode zur Durchführung polynomieller Interpolation suchen, gibt es auch mathematische Software oder spezialisierte Tools, die diese Funktionalität bieten können.

Ein beliebtes mathematisches Softwarepaket ist MATLAB. MATLAB bietet eine integrierte Funktion namens polyfit, die für die polynomiale Interpolation verwendet werden kann. Es gibt auch andere mathematische Software wie Mathematica oder Octave, die ähnliche Funktionen bieten.

Wenn Sie keine Programmiersprache verwenden möchten, könnten Sie auch ein spezialisiertes Software-Tool wie Microsoft Excel verwenden. Excel bietet Funktionen zur Kurvenanpassung, mit denen Sie eine polynomiale Interpolation durchführen können.

Allerdings ist es wichtig zu beachten, dass die Verwendung einer Programmiersprache wie Python oder mathematischer Software wie MATLAB in der Regel die flexibelsten und leistungsfähigsten Möglichkeiten bietet, polynomiale Interpolation durchzuführen und Ergebnisse zu analysieren.

Person:

  • ganz ohne numpy also manuell in python

ChatGPT:

  • Wenn Sie die polynomiale Interpolation manuell in Python ohne die Verwendung von NumPy durchführen möchten, können Sie dies mit den grundlegenden Funktionen und Listen in Python erreichen. Hier ist ein Beispiel:

In diesem Beispiel werden die Datenpunkte in den Listen x und y angegeben. Die Interpolation erfolgt manuell, indem die Koeffizienten des Polynoms berechnet werden. Der Code verwendet eine verschachtelte Schleifenstruktur, um die Koeffizienten einzeln zu berechnen.

Anschließend wird das interpolierte Polynom an einem bestimmten Punkt x_new ausgewertet, indem die Koeffizienten entsprechend der Potenz des Punkts multipliziert werden.

Beachten Sie, dass dieses manuelle Verfahren für polynomiale Interpolation nicht so effizient ist wie die Verwendung von NumPy oder spezialisierten Bibliotheken. Es ist jedoch eine Möglichkeit, dies in Python ohne externe Bibliotheken zu erreichen.

Person:

  • lineares ausgleichsproblem in python ohne bibliotheken

ChatGPT:

  • Wenn Sie das lineare Ausgleichsproblem in Python ohne die Verwendung externer Bibliotheken lösen möchten, können Sie die Methode der kleinsten Quadrate implementieren. Hier ist ein Beispiel:

In diesem Beispiel werden die Datenpunkte in den Listen x und y angegeben. Das lineare Ausgleichsproblem wird gelöst, indem die Koeffizienten (Steigung und Y-Achsenabschnitt) der besten Anpassungslinie berechnet werden.

Der Code verwendet die Formeln für die Methode der kleinsten Quadrate, um die Koeffizienten zu berechnen. Anschließend kann die beste Anpassungslinie an einem bestimmten Punkt x_new ausgewertet werden, indem der Punkt in die lineare Gleichung y = mx + c eingesetzt wird.

Bitte beachten Sie, dass diese Methode für das lineare Ausgleichsproblem funktioniert, aber für komplexere Anpassungsprobleme oder nichtlineare Funktionen möglicherweise nicht ausreicht. In solchen Fällen sind externe Bibliotheken wie NumPy, SciPy oder scikit-learn mit speziellen Funktionen und Algorithmen für die lineare Regression möglicherweise besser geeignet.

Person:

  • rechteckregel in python

ChatGPT:

  • Die Rechteckregel ist eine einfache numerische Methode zur Approximation von Integralen. Hier ist ein Beispiel für die Implementierung der Rechteckregel in Python:

Dieses Beispiel definiert eine Funktion rectangle_rule, die die Rechteckregel verwendet, um das Integral einer gegebenen Funktion f über das Intervall von a bis b zu approximieren. Die Anzahl der Unterteilungen (Rechtecke) wird durch den Parameter n angegeben.

Die Funktion berechnet die Breite jedes Rechtecks als (b - a) / n. Dann wird die Summe der Funktionswerte an den linken Randpunkten der Rechtecke berechnet. Der Ausdruck f(x) gibt den Funktionswert an der Position x zurück. Schließlich wird das Ergebnis mit der Breite multipliziert, um die Approximation des Integrals zu erhalten.

Hier ist ein Beispiel, wie Sie die rectangle_rule-Funktion verwenden können:


In diesem Beispiel wird das Integral der Funktion f(x) = x^2 über das Intervall von 0 bis 1 approximiert, indem die rectangle_rule-Funktion mit den entsprechenden Parametern aufgerufen wird. Das Ergebnis wird dann ausgegeben.

Person:

  • Trapezregel in python

ChatGPT:

  • Die Trapezregel ist eine weitere numerische Methode zur Approximation von Integralen. Hier ist ein Beispiel für die Implementierung der Trapezregel in Python:

Dieses Beispiel definiert eine Funktion trapezoidal_rule, die die Trapezregel verwendet, um das Integral einer gegebenen Funktion f über das Intervall von a bis b zu approximieren. Die Anzahl der Unterteilungen (Trapezoide) wird durch den Parameter n angegeben.

Die Funktion teilt das Intervall in n Abschnitte mit einer Breite von width = (b - a) / n. Für jedes Trapez werden die Funktionswerte an den Randpunkten x0 und x1 berechnet und dann durch 2 addiert. Die Summe dieser Werte wird anschließend mit der Breite multipliziert, um die Approximation des Integrals zu erhalten.

Hier ist ein Beispiel, wie Sie die trapezoidal_rule-Funktion verwenden können:


In diesem Beispiel wird das Integral der Funktion f(x) = x^2 über das Intervall von 0 bis 1 approximiert, indem die trapezoidal_rule-Funktion mit den entsprechenden Parametern aufgerufen wird. Das Ergebnis wird dann ausgegeben.

Person:

  • Sympson regel in python ohne bibliothek

ChatGPT:

  • Die Simpson-Regel ist eine weitere numerische Methode zur Approximation von Integralen. Hier ist ein Beispiel für die Implementierung der Simpson-Regel in Python ohne Verwendung externer Bibliotheken:

Dieses Beispiel definiert eine Funktion simpson_rule, die Simpson's Regel verwendet, um das Integral einer gegebenen Funktion f über das Intervall von a bis b zu approximieren. Die Anzahl der Unterteilungen für die Approximation wird durch den Parameter n angegeben, der eine gerade Zahl sein muss.

Die Funktion verwendet eine Schleife, um das Intervall in n Abschnitte zu unterteilen. Für jedes Teilintervall werden die Funktionswerte an den Randpunkten x0, x1 und x2 berechnet und mit den entsprechenden Gewichtungen addiert. Die Summe dieser Werte wird anschließend mit h/6 multipliziert, um die Approximation des Integrals zu erhalten.

Hier ist ein Beispiel, wie Sie die simpson_rule-Funktion verwenden können:


In diesem Beispiel wird das Integral der Funktion f(x) = x^2 über das Intervall von 0 bis 1 approximiert, indem die simpson_rule-Funktion mit den entsprechenden Parametern aufgerufen wird. Das Ergebnis wird dann ausgegeben.

Person:

  • summierte sympsonregel

ChatGPT:

  • Die summierte Simpson-Regel (auch bekannt als zusammengesetzte Simpson-Regel) erweitert die Grundidee der Simpson-Regel auf mehrere Teilintervalle, um eine genauere Approximation des Integrals zu erhalten. Hier ist ein Beispiel für die Implementierung der summierten Simpson-Regel in Python ohne Verwendung externer Bibliotheken:

In dieser Implementierung wird die Summe der summierten Simpson-Regel verwendet, um das Integral einer gegebenen Funktion f über das Intervall von a bis b zu approximieren. Die Anzahl der Unterteilungen für die Approximation wird durch den Parameter n angegeben, der eine gerade Zahl sein muss.

Die Funktion teilt das Intervall in n Teilintervalle mit einer Schrittweite h = (b - a) / n. Eine Liste x wird erstellt, die die Randpunkte aller Teilintervalle enthält. Dann wird eine Schleife verwendet, um über die Teilintervalle zu iterieren und die entsprechenden Funktionswerte in der summierten Simpson-Regel-Formel zu berechnen und aufsummiert.

Die Summe der Funktionswerte wird anschließend mit h / 3 multipliziert, um die Approximation des Integrals zu erhalten.

Hier ist ein Beispiel, wie Sie die composite_simpson_rule-Funktion verwenden können:


In diesem Beispiel wird das Integral der Funktion f(x) = x^2 über das Intervall von 0 bis 1 mit der summierten Simpson-Regel approximiert, indem die composite_simpson_rule-Funktion mit den entsprechenden Parametern aufgerufen wird. Das Ergebnis wird dann ausgegeben.

Person:

  • der fehler der summierten quadraturformel

ChatGPT:

  • Der Fehler der summierten Quadraturformel (wie die summierte Simpson-Regel) kann mithilfe des Richardson-Extrapolationsverfahrens geschätzt werden. Der Fehler der summierten Quadraturformel ist abhängig von der Anzahl der Teilintervalle und von der Ableitungsordnung der Funktion.

Die Fehlerabschätzung für die summierte Quadraturformel lautet:

E=−(b−a)⋅h4180⋅f(4)(ξ)E = - \frac{{(b - a) \cdot h^4}}{{180}} \cdot f^{(4)}(\xi)E=−180(b−a)⋅h4​⋅f(4)(ξ)

wobei hhh die Schrittweite ist und f(4)(ξ)f^{(4)}(\xi)f(4)(ξ) die vierte Ableitung der Funktion fff an einer Stelle ξ\xiξ im Intervall [a, b].

Es ist jedoch wichtig zu beachten, dass die genaue Berechnung des Fehlers oft schwierig ist, da die vierte Ableitung der Funktion nicht immer bekannt oder leicht zu berechnen ist. Die Fehlerabschätzung bietet jedoch eine gute Richtlinie für die Genauigkeit der Approximation.

Hier ist ein Beispiel, wie Sie den Fehler der summierten Quadraturformel für die summierte Simpson-Regel in Python berechnen können:


In diesem Beispiel wird die Funktion composite_simpson_error verwendet, um den Fehler der summierten Quadraturformel für die summierte Simpson-Regel abzuschätzen. Die Funktion verwendet die vierte Ableitung der Funktion f anhand der Funktion fourth_derivative_func, die die vierte Ableitung numerisch mithilfe von finiten Differenzen berechnet. Der maximale Betrag der vierten Ableitung im Intervall [a, b] wird verwendet, um den Fehler abzuschätzen.

Bitte beachten Sie, dass die numerische Schätzung des Fehlers mit finiten Differenzen möglicherweise nicht immer genau ist und von der Funktion und der Wahl der Schrittweite abhängen kann.

Person:

  • romberg extrapolation

ChatGPT:

  • Die Romberg-Extrapolation ist eine Methode zur Verbesserung der Genauigkeit von numerischen Quadraturverfahren, wie z.B. der Trapezregel oder der Simpson-Regel. Sie basiert auf der Idee, sukzessive approximierte Werte zu berechnen und diese dann zu extrapolieren, um eine genauere Schätzung des Integrals zu erhalten.

Hier ist ein Beispiel für die Implementierung der Romberg-Extrapolation in Python:


Diese Implementierung verwendet eine Matrix r, um die approximierten Werte während des Extrapolationsprozesses zu speichern. Die Romberg-Extrapolation erfolgt in n Iterationen. Zuerst wird der Anfangswert r[0][0] mit Hilfe der Trapezregel berechnet. Dann erfolgen die Extrapolationsschritte, bei denen sukzessive genauere Approximationen berechnet werden. Schließlich wird der Wert r[n][n] als die beste Schätzung des Integrals zurückgegeben.

Hier ist ein Beispiel, wie Sie die romberg_integration-Funktion verwenden können:


In diesem Beispiel wird das Integral der Funktion f(x) = x^2 über das Intervall von 0 bis 1 mit Hilfe der Romberg-Extrapolation approximiert. Die romberg_integration-Funktion wird mit den entsprechenden Parametern aufgerufen und das Ergebnis wird ausgegeben. Je höher die Anzahl der Iterationen n, desto genauer wird die Approximation.

Person:

  • richtungsfeld einer differentialgleichung

ChatGPT:

  • Ein Richtungsfeld ist eine grafische Darstellung von Vektorfeldern, die die Richtungen von Tangenten an Lösungskurven einer Differentialgleichung anzeigen. Es gibt verschiedene Möglichkeiten, ein Richtungsfeld für eine Differentialgleichung zu zeichnen, aber im Folgenden werde ich eine grundlegende Methode beschreiben:
  1. Identifiziere die Differentialgleichung und ihre Unbekannte. Zum Beispiel nehmen wir an, wir haben die Differentialgleichung dydx=x+y\frac{dy}{dx} = x + ydxdy​=x+y mit der Unbekannten y(x)y(x)y(x).
  2. Wähle ein Raster von Punkten im Koordinatensystem, um das Richtungsfeld zu erstellen. Du kannst zum Beispiel eine Gitterstruktur auf dem Bereich der interessierenden xxx- und yyy-Werte definieren.
  3. An jedem Punkt im Raster berechne den Wert der Steigung dydx\frac{dy}{dx}dxdy​ basierend auf der gegebenen Differentialgleichung. Verwende diesen Wert, um einen Vektor von dem Punkt aus zu zeichnen, der in die Richtung der Steigung zeigt.
  4. Wiederhole den Schritt 3 für alle Punkte im Raster. Die resultierenden Vektoren bilden das Richtungsfeld.
  5. Optional: Zeichne Lösungskurven in das Richtungsfeld ein, um eine Vorstellung von den Lösungen der Differentialgleichung zu bekommen. Du kannst dies tun, indem du die Differentialgleichung numerisch löst oder analytisch bekannte Lösungen verwendest.

Hier ist ein Beispielcode in Python, der ein Richtungsfeld für die Differentialgleichung dydx=x+y\frac{dy}{dx} = x + ydxdy​=x+y erstellt:


Dieser Code verwendet die Bibliotheken NumPy und Matplotlib, um das Richtungsfeld zu erstellen. Wir definieren eine Funktion dy_dx(x, y), die die Ableitung dydx\frac{dy}{dx}dxdy​ basierend auf der Differentialgleichung berechnet. Dann erstellen wir ein Gitter von Punkten im Bereich von -5 bis 5 für x- und y-Werte. Die x-Komponente der Vektoren ist konstant 1, und die y-Komponente basiert auf der Funktion dy_dx(x, y). Schließlich verwenden wir die Funktion plt.quiver von Matplotlib, um das Richtungsfeld zu zeichnen.

Das erstellte Richtungsfeld zeigt die Richtungen der Tangenten an Lösungskur

Person:

  • zeichne zudem die lösungskurve für y(0) =3 ein

ChatGPT:

  • Um eine Lösungskurve in das Richtungsfeld einzufügen, können wir die Differentialgleichung numerisch mit einem geeigneten Verfahren lösen. Ein einfaches numerisches Verfahren ist das Euler-Verfahren. Hier ist der aktualisierte Code, der das Richtungsfeld mit der Lösungskurve für die Anfangsbedingung y(0)=3y(0) = 3y(0)=3 zeigt:

In diesem aktualisierten Code haben wir eine Funktion euler_method, die das Euler-Verfahren verwendet, um die Differentialgleichung numerisch zu lösen. Wir rufen diese Funktion auf und erhalten die x- und y-Werte für die Lösungskurve. Dann verwenden wir plt.plot von Matplotlib, um die Lösungskurve in Rot einzufügen.

Jetzt wird das Richtungsfeld zusammen mit der Lösungskurve für y(0)=3y(0) = 3y(0)=3 angezeigt.

Person:

  • das eulerverfahren

ChatGPT:

  • Entschuldigung für das Missverständnis. Hier ist der Code, der das Euler-Verfahren zur Lösung der Differentialgleichung verwendet und die Lösungskurve in das Richtungsfeld einfügt:

Hier wird das Euler-Verfahren verwendet, um die Differentialgleichung dydx=x+y\frac{dy}{dx} = x + ydxdy​=x+y numerisch zu lösen. Die Funktion euler_method führt das Euler-Verfahren aus und gibt die x- und y-Werte für die Lösungskurve zurück. Anschließend wird die Lösungskurve mit plt.plot in Rot in das Richtungsfeld eingefügt.

Das aktualisierte Diagramm zeigt das Richtungsfeld zusammen mit der Lösungskurve für die Anfangsbedingung y(0)=3y(0) = 3y(0)=3.

Person:

  • Runge Kutta Verfahren

ChatGPT:

  • Das Runge-Kutta-Verfahren ist eine beliebte Methode zur numerischen Lösung von gewöhnlichen Differentialgleichungen. Es verwendet eine Kombination von Funktionsauswertungen an verschiedenen Punkten, um die Genauigkeit der Approximation zu verbessern. Hier ist eine Implementierung des klassischen Runge-Kutta-Verfahrens (auch bekannt als RK4) in Python:

Diese Funktion runge_kutta nimmt die Differentialgleichung f, den Anfangswert x0 und y0, die Schrittweite h und die Anzahl der Schritte num_steps. Sie führt das Runge-Kutta-Verfahren aus und gibt die Arrays x und y zurück, die die berechneten x- und y-Werte der Lösungskurve enthalten.

Hier ist ein Beispiel, wie du das Runge-Kutta-Verfahren verwenden kannst:


In diesem Beispiel verwenden wir die Differentialgleichung dy_dx, den Anfangswert y0, die Schrittweite h und die Anzahl der Schritte num_steps, um das Runge-Kutta-Verfahren auf die Differentialgleichung anzuwenden. Die berechneten x- und y-Werte werden dann mit plt.plot in das Diagramm eingefügt.

Das erstellte Diagramm zeigt die Lösungskurve der Differentialgleichung dy/dx = x + y für die Anfangsbedingung y(0) = 3, berechnet mit dem Runge-Kutta-Verfahren.

Person:

  • stabilitätsfunktion

ChatGPT:

  • Die Stabilitätsfunktion ist eine Methode zur Analyse der Stabilität von numerischen Integrationsverfahren für Differentialgleichungen. Es untersucht, wie sich kleine Störungen im Anfangswert oder im Lösungsverlauf des Verfahrens im Laufe der Zeit ausbreiten.

Für das Runge-Kutta-Verfahren (RK4) kann die Stabilitätsfunktion als Polynom dargestellt werden. Hier ist eine Implementierung der Stabilitätsfunktion für das RK4-Verfahren in Python:


Dieser Code berechnet die Stabilitätsfunktion für das RK4-Verfahren und zeichnet den Betrag der Stabilitätsfunktion als Funktion des Realteils des Komplexwerts z. Die Stabilitätsfunktion wird durch das Polynom 1+z+12z2+16z3+124z41 + z + \frac{1}{2}z^2 + \frac{1}{6}z^3 + \frac{1}{24}z^41+z+21​z2+61​z3+241​z4 dargestellt.

Das erzeugte Diagramm zeigt den Betrag der Stabilitätsfunktion für verschiedene Werte von z. Wenn der Betrag der Stabilitätsfunktion kleiner als 1 ist, bedeutet das, dass das Verfahren stabil ist und kleine Störungen im Laufe der Zeit gedämpft werden. Wenn der Betrag größer als 1 ist, bedeutet das, dass das Verfahren instabil ist und kleine Störungen stark anwachsen können.

Es ist wichtig zu beachten, dass die Stabilitätsfunktion nur die absolute Stabilität des Verfahrens bewertet und nicht seine Genauigkeit oder Konvergenzordnung. Es ist üblich, die Stabilitätsfunktion zusammen mit anderen Analysen wie der Konsistenz und Konvergenz zu betrachten, um ein umfassendes Bild der Eigenschaften eines numerischen Integrationsverfahrens zu erhalten.

SNP

Motivation

Effizient

  • C erlaubt es, sehr effizienten Code zu schreiben.
  • Der verbraucht Arbeitsspeicher (Memory Footprint) ist sehr leicht.
  • Die Ausführungsgeschwindigkeit ist hoch, weil Optimierungen zur Compilezeit ausgeführt werden.

Verbreitet

  • C/C++ ist mit grossem Abstand die wichtigste System- und systemnahe Sprache

Systemverständnis

  • C fördert und bedingt das Verständnis für das unterliegende System

Hauptprgrogramm

#include <stdio.h>    //printf
#include <stdlib.h>

int main (void)
{
	printf("Hello world in C\n"); // Newline ist wichtig
	return EXIT_SUCCESS;
}

Datentypen

Datentyp BytesWertbereich
signed char1 -128 bis 127
unsigned char 1 0 bis 255
[signed] short [int]2 -32768 bis 32767
unsigned short [int]20 bis 65636
[signed] int4-2^31 bis 2^31-1
unsigned [int]4 0 bis 2^32-1
[signed] long [int] 8 -2^63 bis 2^63-1
unsigned long [int] 8 0 bis 2^64-1
[signed] long long [int] 8 -2^63 bis 2^63-1
unsigned long [int] 8 -2^63 bis 2^64-1
long double 19 1.2 * 10^4932 bis 1.2 * 10^4932

Standard Typen

Integer

#include <stdint.h>

int8_t signedValue;
uint8_t unsignedValue;

int16_t signedValue;
uint16_t unsignedValue;

int32_t signedValue;
uint32_t unsignedValue;

int64_t signedValue;
uint64_t unsignedValue;

Bool

#include <stdbool.h>

bool trueValue = true;
bool falseValue = false;

Literalle / Konstanten

  • Im Code eingefügte, unveränderliche Werte
  • Ist die Zahlt zu gross für int, wird diese als long interpretiert.

int decimal = 3987; // beginnt mit 1 - 9

unsigned int octal = 037;  // starts with 0

unsigned int hexadecimal = 0x23; // starts with 0X or 0x

char asciiCode = 'A'; // single ascii code 

double decimalPoint = 1.23;

double exponent = 1.6e3;

Explizite Angabe


int interpretedAsLong = 134L;

int interpretedAsLongLong = 134LL;

// U kann alleine, oder zusätzlich zu L / LL hinzufgefügt werden.
int interpretedAsUnsigned = 134U;

float interpretedAsFloat = 1.23F; // f geht auch

double interpretedAsDouble = 1.23L; // Long float

Zeichenliterale

 Zeichen Beschreibung
\nnew line
\thorizontal tab
\backslash
\' Single quote
\" double quote
 \0 Ascii Code 0

Ascii Tabelle

Strings

Durch Anführungszeichen eingeschlossene Zeichenfolgen werden als strings interpretiert.

char[] iAmString = "Hello World!";

Symbolische Konstanten

Symbolische Konstanten werden Textmässig ersetzt.

#define MAX_LENGTH 1000

Anwendung

#define MAX_LENGTH 1000

int length = MAX_LENGTH; // MAX_LENGTH wird durch den Wert im define ersetzt

Definitionen

  • Sind Variablen ausserhalb von Funktionen definiert, sind diese lokal in der Source (.c) Datei.

Variablen

Eine Variable sollte immer auf einen Wert initialisiert werden, da es keine Standardwerte gibt.


double hoehe;
int laenge, breite; // mach das nicht
double r,
	radius = 15.0;  // Besser so, dann ist es klar, dass nur radius auf 15.0 ist

Konstanten

Konstanten sind nicht veränderlich.

const double pi = 3.14159;

Typ Alias

Erlaubt es eigene Typen zu definieren, oder auch Synonyme.

typedef int Index; // sagt, dass Index von typ int ist.

Index index = 0; // Index kann jetzt als Typ verwendet werden
typedef struct position
{
	int x;
	int y;
} Position;

Position position;
position.x = 1;
position.y = 2;

Statisch

Statische Variablen sind prozessübergreifend.

static int value;
  • Wenn im Header definiert, dann sind diese sobald diese inkludiert wird, verfügbar.
  • Wenn in der Source Datei definiert, dann sind diese in der gleichen Datei in allen Prozessen verfügbar.

Bit-Operatoren

Vorrang und Reihenfolge

Type Casts

Ein type cast wird benötigt, um ein typ explizit in einen anderen Typen zu wandeln

int value = 5;
int divider = 3;
double result = (double) value / divider;

Kontrollstrukturen

If-Else / Else-if

const int SOME_CONSTANT = 10;
int value = 5;
if (value > SOME_CONSTANT) {
	// Do something if greater than SOME_CONSTANT
} else if (value < SOME_CONSTANT) {
	// Do something if less than SOME_CONSTANT
} else {
	// Do something if equal to SOME_CONSTANT
}

For-Schleife

for (int i=1; i <= 5; i++) {
	// Mache etwas in der for schleif
}

While

Zuerst wird die Bedingung geprüft, dann der Inhalt der Schleife ausgeführt

int i=0; // Initialisierung its wichtig
while (i < 5)
{
	// Mache etwas in der for schleife
	i++; // i muss manuell verändert werden
}

Do while

Im Gegensatz zur While schleife, wird in der Do-While der Inhalt der Schleife zuerst ausgeführt.

int i=0; // Initialisierung its wichtig
do
{
	// Mache etwas in der for schleife
	i++; // i muss manuell verändert werden
	// WIRD MINDESTENS 1 MAL AUSGEFÜHRT
}
while (i < 5);

Switch

Es können keine Strings evaluiert werden!!

int n;
switch (n)
{
case 1:
{
	// Wenn 1
	break;
}
case 2:
case 3:
{
	// Wenn 2 oder 3
	break;
}
default:
{
	// Alles andere
	break;
}

}

Input

stdout / stdin / stderr sind auf der Konsole

Konstanten String auf StdOut ausgeben


#include <stdio.h>

int result = puts("Hello world");
if (result == EOF) 
{
	// Konnte nicht geschrieben werden
}

Einzelnes Zeichen auf StdOut ausgeben


#include <stdio.h>

int result = putchar('A');
if (result == EOF) 
{
	// Konnte nicht geschrieben werden
}

Einzelnes Zeichen von StdIn lesen


#include <stdio.h>

char c = getchar();

// Das Zeichen wird in c gespeichert

Formatierte Ausgabe / Eingabe

#include <stdio.h>

printf(format, arg1, arg2, . . .); // für Ausgabe

scanf(format, &x, &y, &z); // Parameter (ausser string (char*)) mit & angeben

Der Parameter format ist ein String. Jedes der zusätzlichen Argumente (arg1, arg2, …) ist in diesem Forhanden und kann folgedes bedeuten.

Zeichenfolge Bedeutung
%d, %iint
%uunsigned int
%cchar
%sString (char *)
%ffloat

Formatierung von Float

%m.df

SymbolBedeutung
mMinimale Länge des Outputs
dAnzahl dezimalstellen
#include <stdio.h>
double value = 5.12345;

printf("%f", value); //5.123450 (dezimal standard=6)

printf("%.3f", value); //5.123

printf("%10.3f", value); //      5.123 (6 Leerzeichen)

Enums

enum Wochentage
{
	Monday,  // int wert ist 0
	Tuesday, // int wert ist 1, nachfolgende sind vortlaufend
	Wednesday,
	Thursday = 13, // int wert ist 13 
	Friday, // int wert ist 14
	Saturday,
	Sunday
};

// Direkt auf Monday zugreifen, keine Angabe des Namespaces notwendig
Wochentag tag = Monday;


int dayOfWeek = tag; // 

Strukturen / Datenklassen

struct Point3D
{
	double x;
	double y;
	double z;
}

struct Point3D ptA = {1.0, 3.0, 4.0}; // Standard initialisierung

ptA.x = 5.0; // Zugriff

Mit Typedef

typedef struct
{
	double x;
	double y;
	double z;
} Point3D;

Point3D ptA = {1.0, 3.0, 4.0}; // Standard initialisierung

ptA.x = 5.0; // Zugriff

Arrays werden als ANEINANDERLIEGENDE Blocks im Memory definiert.

Länge: Die Länge des Arrays MUSS zur Kompilierzeit bekannt sein. Index: Beginnt bei 0

const int ARRAY_LEN = 6;
int[ARRAY_LEN] array = { 0 }; // Alle Werte auf 0 setzem

Modifzieren des Arrays

const int ARRAY_LEN = 6;
int[ARRAY_LEN] array = { 0 }; // Alle Werte auf 0 setzem

array[2] = 6; // setzte 3. Element auf 6

// AUFPASSEN BEI DER INDEXIERUNG
// Es wird keine IndexOutOfRange Exception geworfen, wenn ausserhalbt des Blockes geschrieben wird
arrray[12] = 7;
array[-3] = 9;

Initialisierung eines Arrays

int array[] = { 1, 2, 3}; // alloziert einen Array der Länge 3

Länge eines Arrays

Bevorzug

const size_t ARRAY_LEN = 100;

int array[ARRAY_LEN] = { 0 };

for (size_t i =0; i < ARRAY_LEN; i++) {
	array[i] = i;
}

„Dynamische“ Länge


int array[100] = { 0 };

// Nicht in einer Funktion möglich!!
size_t len = sizeof(array) / sizeof(array[0]);

for (size_t i =0; i < len; i++) {
	array[i] = i;
}

Mehrdimensionale Arrays

Wie ein Schachbrett


int array[2][3] = {
	{ 1, 2, 3 },
	{ 4, 5, 7}
};

array[0][0] = 5; // Zugriff

Char Arrays und Strings

In Format: %s

String terminator: Ein char array wird immer mit \0 beendet, ansonsten ist es unmöglich die Länge zu bestimmen. -> Also Grösse immer um 1 erhöhen als gewünschte Länge

char hello[] = "hello, world"; // kann modifziert werden

/*
h
e
l
l
o
,

w
o
r
l
d
\0
*/

Es ist aber möglich, den String-Terminator zu entfernen, indem man folgendes macht.


char[12] hello = "Hello, World";

String Funktionen

#include <string.h>
char[] hello = "Hello world";

// String läng bestimmen
int laenge = strlen(hello);

// String vergleich
int result = stcmp(hello, "Hi");
// 0 = beide sind gleich
// > 0 wenn der erste string einen grösseren Ascii Wert hat als der zweite
// < 0 wenn der zweite string einen grösseren Ascii wert hat als der erste

String kopieren

Kopiert source in dest, egal ob die Länge passt. Rückgabewert ist ein Pointer zur destination

char* strcpy(char dest[], const char source[]);

Kopiert source in dest, mit der maximalen Länge num Rückgabewert ist ein Pointer zur destination

char* strncpy(char dest[], const char source[], size_t num);

Strings aneinanderhängen

Hängt eine Kopie von source an destination.

char * strcat ( char * destination, const char * source );

Hängt die ersten num Zeichen von source an destination.

char * strncat ( char * destination, const char * source, size_t num );

Wird benötigt um Aufgaben aufzutrennen, und zu vereinfachen

Datei endet mit .h

// list.h
#ifndef __LIST_H__
#define __LIST_H__

// Diese Defines sind wichtig, da sonst der Header doppelt kompiliert werden kann. Macht auch Probleme mit Defines, Konstanten, statics und Methodendeklarationen


// Methode wird im Header deklariert
// so weiss jedes andere Modul, heyy in dieser Datei finde ich die deklaration
// ausprogrammiert wird diese aber in der source (.c) datei
void list_clear(void);

// Diese Variable ist GLOBAL in ALLEN Dateien, die list.h inkludieren vorhanden
static int iAmGlobal = 0;

#endif __LIST_H__

Können aber auch durch folgendes ersetzt werden

// list.h
#pragma once

Source

// list.s

#include "list.h"

// ist für jedes include separat definiert
int iAmSpecific = 3;

// ist für jedes include gleich, kann für Prozesskommunikation verwendet werden.
static int iAmProcessWide = 5;

void list_clear()
{
	// globale variable modifizieren
	iAmGlobal = 2;

	// verändert die Dateispezifische Variable
	iAmSpecific = 5;
}

Makefile

Du gucken da: Makefile cheatsheet

Pointers zeigen auf eine Adresse im Speicher.

int *p; // Pointer auf ein Objekt vom typ int

char *c[20]; // Array von 20 Pointern auf Objekte vom typ char

double (*d)[20]; // Ist ein Pointer, der af ein Array mit 20 Elementen zeigt

char **ppc; // Zeigt auf ein Objekt vom Typ Pointer
			// das auf ein Objekt vom Typ char zeigt

int * p, q; // p ist pointer
			// q ist KEIN pointer

Sizeof: Systemarchitektur abhängig (32 bit = 32/8 -> 4 byte)

Operatoren

int i;

int *p;
int *op;

p = &i;  // p zeigt auf die Adresse von i

*p = 4; // modifiziert den Wert von i

// Zeigt auf die gleiche Adresse wie p zum ZEITPUNKT des setzens.
op = p;
*op += 5; // modifiziert Objket i

Pointer müssen immer auf etwas zeigen, sonst wird IRGENDEIN Speicher modifizert.

Der alles mögliche Pointer (Void pointer)

double d = 1.0;

// Void pointer, zeigt auf alles mögliche
void *vp = &d;

// Zuerst auf double pointer casten
// dann wert von double auslesen
printf("%f", * (double*)vp);

Konstante Pointers

Konstant = nicht mehr modifizierbar

int i = 15;
int * const p = &i;

// i kann modifizert werden
i = 16;
printf("%d", *p);

// p aber nicht mehr
p++; // DAS GEHT NICHT!!!

NULL Pointer

Hat die Adresse 0

#include <stdio.h>

int *p1 = 0; //blöde idee, vorallem nicht lesbar
int *p2 = NULL; // perfekt

Für strukturen


typedef struct {
  int studNr;
  char name[30];
  char vorname[30];
} Student;

Student notPointer;
Student *pointer;


notPointer.studNr = 4;
strcpy(notPointer.name, "Mueller");

// Hey, ich zeige auf den notPointer
pointer = &notPointer;

printf("%s", (*pointer).name); // Mueller

Pointer Arithmetik

int a[5] = {2, 4, 6, 8, 10};
int *p = a;


// alle folgenden Zeilen sind identisch
a[3]     = 1; // vom Compiler intern als *(a + 3) = 1 generiert
*(a + 3) = 1; // a steht für die Start Adresse von a
*(p + 3) = 1; // p ist auf die Start Adresse von a gesetzt
p[3]     = 1; // vom Compiler intern als *(p + 3) = 1 generiert

Iterator

int a[5];

// a+5 ist die Adresse *hinter* das
// letzte Element des Arrays: C++ Stil

for(int *it = a; it != a+5; ++it) {
	*it = 0;
}

oder

int array[5];

int *it = &array[0]; // zeigt auf das erste Element von a
int *end = &array[5]; // zeigt auf das letze Element von a
    
for(it; it != end; ++it) {
	*it = 0;
}

String Literale


char * str = "I am a literal";

printf("%s", str);

printf("%c", str[0]);

Jagged Arrays

Eindimensionale Arrays von pointern.

char *jagged[] = {
	"January",
	"February",
	"March",
	"April",
	"Mai",
	"June",
	"July",
	"August",
	"September",
	"October",
	"November",
	"December" };


if (jagged[3][2] == 'r') {
...
}
// jagged[3] is “April”
// jagged[3][2] is the third character of April

POINTER WIRD ÜBERGEBEN


void swap (int *a, int *b)
{
	int saved_a = *a; // Wert von a zwischenspeichern
	*a = *b;
	*b = saved_a; // WIHCTIG: Wert anpassen, und nicht Adresse
}

Read only

void print_array(const int *a, int n)
{
	// const verhindert die Modifikation von a
}

Arrays übergeben

void print_array(int *a, int n)
{ }

ist gleich wie

void print_array(int a[], int n)
{ }

Structs übergeben

void do_something(struct *s)
{ }
void do_something(const struct *s)
{
	// struct kann nicht überschrieben werden
}

Heap

Zur Laufzeit dynamisch Speicher vom Heap anfordern

// aus stdlib.h
// alloziert "size" Bytes vom Heap und gibt die Start Adresse zurück
void *malloc(size_t size);
// alloziert "nitems" mal "size" Bytes, setzt sie auf 0, und gibt die Adresse zurück void *calloc(size_t nitems, size_t size);
// vergrössert (oder verkleinert) einen vorgängig angeforderten Speicherbereich
void *realloc(void *ptr, size_t size);
// gibt einen oben angeforderten Speicherbereich frei
void free(void *ptr);

Beispiel

#include <stdlib.h>

typedef struct {
	char firstName[30];
	char lastName[30];
} Person;

Person* pointerToPerson = (Person*)malloc(sizeof(Person));

if (pointerToPerson == NULL) {
	// Etwas ist schief gelaufen. Pointer kann NICHT verwendet werden!!
	return;
}

// Wandlung in Person, dann firstname setzen
strcpy((*pointerToPerson).firstName, "firstname");

// Direkter Speicherzugriff
strcpy(pointerToPerson->lastName, "lastName");


printf("%s", pointerToPerson->firstName);

printf("%s", (*pointerToPerson).lastName);

//GANNZ WICHTIG
free(pointerToPerson); // Speicherplatz wieder frei geben

Probleme

Buffer Overflow

  • z.B. Schreiben von 21 Zeichen langem String in 20 Zeichen langen Array,

Datei öffnen

Eine Datei kann in unterschiedliche Modis geöffnet werden.

ModusBeschreibungDatei nicht vorhanden
rASCII lesenNULL
rbBinär lesenNULL
wASCII schreibenErstellt Datei
wbBinäres schreibenErstellt Datei
aAnfügenErstellt Datei
abBinäres AnfügenErstellt Datei
r+Lesen und schreibenNULL
rb+Lesen und schreiben in binärNULL
w+lesen und schreibenErstellt Datei
wb+Lesen und schreiben in binärErstellt Datei
ab+Anfügen und lesen in binärErstellt Datei
#include <stdio.h>
#include <stdlib.h>

FILE *f = fopen("Pfad", "mode");

Datei lesen (line by line)


#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char * line = NULL;
	size_t len = 0;
	ssize_t read;
	FILE *f = fopen("Pfad", "r");
	
	if (f == NULL) { // das gleiche wie if (!f)
		perror("Datei konnte nicht geöffnet werden!");
		exit(-1);
	}
	
	while ((read = getline(&line, &len, f)) != -1) {
		printf("Retrieved line of length %zu:\n", read);
		printf("%s", line);
	}
	
	// Datei wieder schliessen
	fclose(f);
	
	if (line)
	{
		free (line);
	}
	return EXIT_SUCCESS;
}

Datei schreiben (by Charater)


#include <stdio.h>
#include <stdlib.h>


int main(void)
{
	char * line = NULL;
	size_t len = 0;
	ssize_t read;
	FILE *f = fopen("Pfad", "w");
	
	if (f == NULL) { // das gleiche wie if (!f)
		perror("Datei konnte nicht geöffnet werden!");
		exit(-1);
	}
	// Datei wieder schliessen
	fclose(f);
	
	if (line)
	{
		free (line);
	}
	return EXIT_SUCCESS;

}

Datei anfügen


#include <stdio.h>
#include <stdlib.h>

int main(void)
{	
	char * line = NULL;
	size_t len = 0;
	ssize_t read;
	FILE *f = fopen("Pfad", "a");
	
	if (f == NULL) { // das gleiche wie if (!f)
		perror("Datei konnte nicht geöffnet werden!");
		exit(-1);
	}
	// Datei wieder schliessen
	fclose(f);
	
	if (line)
	{
		free (line);
	}
 	return EXIT_SUCCESS;
}

Multi-Tasking

Task: Eine Aufgabe die von der CPU abgearbeitet wird Batch-Ausführen: Führ die anstehenden Tasks nacheinander durch. Multi-Tasking: Führt die Prozesse gleichzeitig / nebeneinander aus

Scheduling

Kooperativ: Jeder Task entschieden, wann sie die Kontrolle an einen anderen Task abgibt. Präemptiv: Kontrollübergabe an den nächsten Task wird erzwungen Scheduler

  • Unterbricht präemtiv
  • entscheidet welche Tasks dran sind
    • Round robin (im Kreis herum)
    • Priority driven

Fork

Nach dem fork existiert der Kindprozess als Kopie des original Prozesses.

  • Bisherige Variablen werden also exakt kopiert
  • Somit sind auch offene Files dann „doppelt“ vorhanden. Also immer aufpassen
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define PERROR_AND_EXIT(M) do { perror(M); exit(EXIT_FAILURE); } while(0)

int main()
{

    // Erstelle neuen Child prozess
    // setzt DANACH die childId
    pid_t childId = fork();
    if (childId == -1)
    {
      PERROR_AND_EXIT("fork");
    }
  
    if (childId > 0)
    {
        // ChildId ist gesetzt, also sind wir im Parent
        printf("Parent: %d forked child %d\n", getpid(), childId);
    
        int wstatus;

        // Jetzt warten wir, bis der Prozess des Kindes
        // fertig ist, 0 -> kein timeout also unendlich lange
        pid_t wpid = waitpid(childId, &wstatus, 0);
        printf("Child exited with status: %d \n", WEXITSTATUS(wstatus));

        // jetzt beenden wir den Parent mit Success
        exit(EXIT_SUCCESS);
    }
    else
    {
        // Da childId noch nicht gesetzt ist, zum Zeitpunkt des forks
        // Wissen wir, dass wir im child sind


        printf("Child: %d forked by parent %d\n", getpid(), getppid());
        sleep(3);
        exit(123);
    }
}

// Parent erstellt neues Kind
// Kind wird ausgeführt, blockiert parent bis abgeschlossen (exit)
// Parent wird abgeschlossen (exit)

Exec

Ersetzt den Childprozess durch ein anders ausführbares Programm.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define PERROR_AND_EXIT(M) do { perror(M); exit(EXIT_FAILURE); } while(0)

int main()
{
    // Erstelle neuen Child prozess
    // setzt DANACH die childId
    pid_t childId = fork(); // Erstelle neues Kind
    if (childId == -1)
    {
        PERROR_AND_EXIT("fork");
    }
    
    if (childId > 0)
    {
        // ChildId ist gesetzt, also sind wir im Parent
        printf("Parent: %d forked child %d\n", getpid(), childId);
    
        int wstatus;

        // Jetzt warten wir, bis der Prozess des Kindes
        // fertig ist, 0 -> kein timeout also unendlich lange
        pid_t wpid = waitpid(childId, &wstatus, 0);
        printf("Child exited with status: %d \n", WEXITSTATUS(wstatus));

        // jetzt beenden wir den Parent mit Success
        exit(EXIT_SUCCESS);
    }
    else
    {
        // Da childId noch nicht gesetzt ist, zum Zeitpunkt des forks
        // Wissen wir, dass wir im child sind


        // Wir definieren die Befehle einzel
        // in diesem fall „ls -l“
        static char *eargv[] = { "ls", "-l", NULL };
        
        if (execv("/bin/ls", eargv) == -1)
        {
            PERROR_AND_EXIT("execv: /bin/ls");
        }

        // Das exec denn Prozess ersetzt, wird diese Zeile nie getroffen
    }
}

System

  1. Spaltet einen Prozess ab fork())
  2. startet die System-Shell, führt in dieser Shell das angegebene Kommando aus
  3. Wartet blockierend auf das Terminieren des Prozesses (waitpid())

WEXITSTATUS

Extrahiert den Exitcode aus dem zurückgegebenen Status des system() Befehles

int ret = system("/bin/ls -l");

POPEN

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define PERROR_AND_EXIT(M) do { perror(M); exit(EXIT_FAILURE); } while(0)

const static int BUFSIZE = 100; 

int main()
{
    // 2 = error pipe in null schreiben, also entsorgen
    FILE *df = popen("df -k --output=pcent . 2>/dev/null", "r");

    if (!df) {
        PERROR_AND_EXIT("popen: df -k .");
    }

    char line[BUFSIZE];
    char *end = NULL;

    long int used = -1;

    while (fgets(line, BUFSIZE, df)) {
        used = strtol(line, &end, 10);
        if (end && end != line && *end == '%') {
            break;
        }
        used = -1;
    }

    if (pclose(df)) {
        PERROR_AND_EXIT("failed with pclose()");
    }

    if (used < 0 || used > 100) {
        errno = ERANGE;
        PERROR_AND_EXIT("df -k .");
    }

    char *msg
        = used < 60 ? "Plenty of disk space (%d%% available) \n"
        : used < 80 ? "Maybe some future disk space problems (%d%% available) \n"
        : used < 90 ? "Need to clear out files (%d%% available) \n"
        : "You may face soon some severe disk space problems (%d%% available) \n";

    printf(msg, 100-used);
    return EXIT_SUCCESS;
}

SO BAUEN

gcc -pthread t08/popen.c

Threads

Da das OS die Threads selber verwaltet, kann es sein, dass die Ausgabe auf der Konsole sich verändert. Wenn jetzt z.B. der Child Prozess eine höhere Priorität hat, dann ist es möglich, dass dieser VOR dem Main prozess endet.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>

#define PERROR_AND_EXIT(M) do { perror(M); exit(EXIT_FAILURE); } while(0)

// Definiere den worker thread, diese Funktion
// wird im thread später ausgeführt
void *worker(void *arg)
{
    printf("worker\n");
    sleep(3);
    static int ret_value = 123;
    return &ret_value;
}

int main()
{
    pthread_t thread;

    // erstelle neuen thread
    // thread -> speichere diesen in die thread adresse
    // NULL -> pthread_attr_t: erlaubt also einige Attribute zu setzen
    // worker -> welche Funktion soll ausgefüht werden
    // NULL -> argument zur Funktion, da kann auch alles mögliche drin stehen
    
    int status = pthread_create(&thread, NULL, worker, NULL);
    if (status != 0)
    {
        // Thread konnte nicht erstellt werden
        exit (status);
    }

    printf("main\n");
    
    static void *retVal;
    status = pthread_join(thread, &retVal);
    if (status != 0)
    {
        // Thread konnte nicht beigetreten werden,
        // es ist etwas schief gelaufen
        exit (status);
    }

    printf("worker retval = %d\n", *((int*)retVal));
    exit (EXIT_SUCCESS);
}

DU SO BAUEN

gcc -pthread t08/threads.c

Loslassen eines Threads (Detach)

Funktion: pthread_detach() Gibt an, dass sobald der Thread beendet wird, die Ressourcen freigegeben werden. Normalerweise passiert dies erst beim pthread_join.

-> Es darf NICHT mit pthread_join auf den Prozess gewartet werden, da dieser bereits weg sein kann.

Terminierung eines Threads

  • return: Standardmässig, der Rückgabewert wird bei pthread_join zurückgegeben.
  • pthread_exit: so ist es möglich, den thread zu beenden. Der Rückgabewert wird bei pthread_join gelesen.
  • exit beendet den übergeordneten Prozess
  • pthread_cancel: Wird verwendet um den Prozess zu beenden.

Kommunikation zwischen zwei Prozessen

POSIX Signale

Diese Signale können jedem Prozess gesendet werden. Somit sind die nachfolgenden Signale universal Anwendbar.

Mit htop können alle Prozesse unter Linux angesehen werden. Dieser dient als zudem als Task-Manager und erlaubt es Prozessen ein bestimmtes POSIX Signal zu senden.

htop
SignalDefault AktionBeschreibung
SIGINTTerminiert Interrupt Signal von der Tastatur (Ctrl-C)
SIGQUIT Code Dump Quit-Signal von der Tastatur (Ctrl-\)
SIGABRTCode DumpAbort Signal via abort() oder assert()
SIGKILL TerminiertKill-Signal
SIGSEGVCode DumpUnzulässiger Speicherzugriff
SIGALRMTerminiertTimer-Signal durch alarm() ausgelöst
 SIGTERMTerminiert Terminierungs-Signal
SIGSTOP Stoppt den prozessStoppt den Prozess (oder ignoriert falls gestoppt)
 SIGCONTReaktiviert den ProzessReaktiviert den Prozess (oder ignoriert falls am laufen)

Beenden des Prozesses

#include <sys/types.h>
#include <signal.h>


// sag dem Childprozess, dass es normal (gracefully) beenden soll
// speichert alles und beendet dann
int status = kill(child_pid, SIGTERM);

if (status != 0)
{
	// Prozess konnte nicht beendet werden
}

Signale verarbeiten

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>


static pid_t start_child(int wait_for_signal)
{
    pid_t cpid = fork();
    if (cpid == -1)
    {
        perror ("Fehler beim fork");
        exit(EXIT_FAILURE);
    }

    if (cpid > 0)
    {
        // Der Parent gibt die child id zurück
        // child hat cpid von 0
        return cpid;
    }

    // Nur der Child Prozess kommt soweit
    if (wait_for_signal)
    {
        // Nur der child 2 kommt soweit
        if (pause() == -1)
        {
            perror("Fehler bei pause()");
            exit(EXIT_FAILURE);
        }
    }
    

    exit (123);
}

static void wait_for_child()
{
    int wsts;

    // bllockiere bis ein Child Prozess beendet ist
    pid_t wpid = wait(&wsts);

    if (wpid == -1)
    {
        perror("Fehler in wait");
        exit(EXIT_FAILURE);
    }

    if (WIFEXITED(wsts))
    {
        printf("Child %d: exit=%d (status=0x%04X)\n", wpid, WEXITSTATUS(wsts), wsts); 
    }

    if (WIFSIGNALED(wsts))
    {
        printf("Child %d: signal=%d (status=0x%04X)\n", wpid, WTERMSIG(wsts), wsts);
    }
}

int main()
{
    pid_t cpid1 = start_child(0); // beendet mit exit code
    pid_t cpid2 = start_child(1); // beendet mit signal

    sleep(1);

    // sag dem Prozess2 dass er sich beenden soll
    if (kill(cpid2, SIGTERM) == -1)
    {
        perror("Prozess 2 konnte nicht beendet werden");
        exit(EXIT_FAILURE);
    }

    wait_for_child();
    wait_for_child();
}



POSIX PIPE

  • Dient als FIFO (First In First Out) Buffer von fixer Maximalgrösse.
  • Unidirektional, von einer zu der anderen Seite.

Mit dem Pipe befehl wird eine Unidirektionale Verbindung erstellt.


int fd[2];

pipe(fd);

// Lese Seite der Pipe
fd[0];

// Schreibseite der Pipe
fd[1];

// schliessen der Read Pipe
close(fd[0]);

// Daten auf Schreib Pipe senden
// 12: länge des zu sendenden strings
write(fd[1], "Hello world\n", 12);


// Lesen von Read Pipe
// Geschieht erst, nachdem alle Kopien
// Des Write Buffers geschlossen worden sind
// Somit ist dies: BLOCKING
read(fd[0], buffer, BUFFER_SIZE);

Anonymous Pipe


#include <stdlib.h> 
#include <stdio.h> 
#include <sys/types.h>
#include <unistd.h>

const int BUFFER_SIZE = 100;

void main (void)
{
    int fd[2];
    char buffer[BUFFER_SIZE];
    ssize_t nBytes;
    int status;

    status = pipe(fd);
    if (status == -1)
    {
        perror("Pipe konnte nicht erstellt werden");
        exit(EXIT_FAILURE);
    }

    pid_t child_id = fork();

    if (child_id == -1)
    {
        perror("Child Prozess konnte nicht erstellt werden");
        exit(EXIT_FAILURE);
    }

    if (child_id == 0)
    {
        // Wir sind in Child Prozess

        // Da wir nichts mit der Schreibpipe zu tun haben, 
        // können wir diese schliessen
        close(fd[1]);

        // Lese Daten von readpipe in buffer
        // Maximale Länge BUFFER_SIZE
        nBytes = read(fd[0], buffer, BUFFER_SIZE);

        // Wir haben unser Zeugs mit der readpipe gemacht,
        // jetzt können wir die pipe schliessen
        close(fd[0]);

        exit(EXIT_SUCCESS);
    }

    if (child_id != 0)
    {
        // Sind in Parent Prozess
        // Dieses if ist eigentlich nicht mehr notwendig

        // Wir wollen nichts lesen,
        // daher können wir in diesem Prozess die Pipe zumachen
        close(fd[0]);

        // Jetzt schreiben wir unsere Daten auf
        // die Lesepipe
        write (fd[1], "Hello world\n", 12);

        // Schliessen der Pipe
        close(fd[1]);

        exit(EXIT_SUCCESS);
    }
}



Non Blocking Version

In machen Fällen ist es notwendig, dass wir die Lesevorgänge der Pipe nicht blockierend machen.

#include <stdlib.h> 
#include <stdio.h> 
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

const static int BUFFER_SIZE = 100;

void set_nonblocking(int fd)
{
    // Lese alle bisher gesetzten Flags
    // Wir wollen nur das nonblocking flag setzen
    int flags = fcntl (fd, F_GETFL, 0);

    if (flags == -1)
    {
        perror("Pipe Attribute konnten nicht gelesen werden");
        exit(EXIT_FAILURE);
    }

    int result = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    if (result == -1)
    {
        perror("Pipe Attribute konnten nicht gesetzt werden");
        exit(EXIT_FAILURE);
    }
}

int main (void)
{
    char buffer[BUFFER_SIZE];
    int fd[2];

    if (pipe(fd))
    {
        perror("Pipe konnte nicht erstellt werden");
        exit(EXIT_FAILURE);
    }

    // pipe auf non blocking setzen
    set_nonblocking(fd[0]);


    // HOWTO LESEN
    int reading = 1;
    while (reading)
    {
        int n = read(fd[0], buffer, BUFFER_SIZE);
        
        if (n == 0)
        {
            // polling aufhören
            // da pipe geschlossen worden ist
            close(fd[0]);
            reading = 0;
            continue;
        }

        if (n > 0)
        {
            // gelesene Daten verwenden
        }
        else if (n = EAGAIN)
        {
            // poll interval abwarten
            // Es kann passieren, dass wir den Intervall so kurz halten
            // dass wir das Lock der pipe nicht wirklich frei lassen
            // dies wird als spin-lock bezeichnet 
        }
        else
        {
            // Da haben wir verkackt
            // Ernsthafter fehler
        }
    }

}

Named Pipes

Named pipes erlauben es, dass zwei unterschiedliche (sich nicht kennende) Prozesse eine Pipeverbindung aufbauen.

mkfifo pfad -m Mode
#include <sys/types.h>
#include <sys/stat.h>


int fd = mkfifo("Pfad", mode_t modus);

// Jetzt kannst du diesen Buffer
// wie in FileIO mit 
// - fread
// - fwrite
// - fclose
// verwenden

POSIX Message Queue

Maximale Kapazität von NxM Bytes - N: Maximale Anzahl Messages - M: Maximale Grösse einer Message (Bytes)

Mehrere Leser und Schreiber

  • Jede Meldung wird beim lesen aus der Queue entfernt

Der name der Queue zeigt auf eine Datei im Filesystem. Daher sollte diese auch mit einem „/„ anfangen, um immer die gleiche Queue zu verwenden.


// Öffnen einer Queue
// soll diese für alle gleich sein, dann / vorne anfügen,
// ansonsten ist die pipe relativ dem aktuellen Verzeichniss

mqd_t mqdes = mq_open ("/sideshow-bob", O_RDWR | O_CREAT, 
                   0664, NULL);


// Öffnen einer Queue mit Attributen

Nachfolgendes Beispiel ist von hier

Publisher

/*
 * Example used in following article:
 *
 * Implementace front zpráv podle normy POSIX
 * https://www.root.cz/clanky/implementace-front-zprav-podle-normy-posix/
 */

#include <stdio.h>
#include <string.h>

#include <mqueue.h>

#define QUEUE_NAME "/queue1"

int main(void)
{
    mqd_t message_queue_id;
    unsigned int priority = 0;
    char message_text[100];
    int status;

    message_queue_id = mq_open(QUEUE_NAME, O_RDWR | O_CREAT | O_EXCL, S_IRWXU | S_IRWXG, NULL);
    if (message_queue_id == -1) {
        perror("Unable to create queue");
        return 2;
    }

    strcpy(message_text, "Hello world!");

    status = mq_send(message_queue_id, message_text, strlen(message_text)+1, priority);
    if (status == -1) {
        perror("Unable to send message");
        return 2;
    }

    status = mq_close(message_queue_id);
    if (status == -1) {
        perror("Unable to close message queue");
        return 2;
    }

    return 0;
}

Subscriber

/*
 * Example used in following article:
 *
 * Implementace front zpráv podle normy POSIX
 * https://www.root.cz/clanky/implementace-front-zprav-podle-normy-posix/
 */

#include <stdio.h>
#include <string.h>

#include <mqueue.h>

#define QUEUE_NAME "/queue1"

int main(void)
{
    mqd_t message_queue_id;
    char message_text[10000];
    unsigned int sender;
    int status;

    message_queue_id = mq_open(QUEUE_NAME, O_RDWR);
    if (message_queue_id == -1) {
        perror("Unable to open queue");
        return 2;
    }

    status = mq_receive(message_queue_id, message_text, sizeof(message_text), &sender);
    if (status == -1) {
        perror("Unable to receive message");
        return 2;
    }
    printf("Received message (%d bytes) from %d: %s\n", status, sender, message_text);

    status = mq_close(message_queue_id);
    if (status == -1) {
        perror("Unable to close message queue");
        return 2;
    }

    return 0;
}

POSIX Queue

#include <mqueue.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

#define PERROR_AND_WAIT(M) do { perror(M); exit(EXIT_FAILURE);} while(0)

// Zeigt auf das root verzeichnis
#define QNAME "/demo" 
#define MSIZE 10


int main(void)
{
    int q = 0;
    int cpid = 0;
    int n = 0;
    int wpid = 0;

    struct mq_attr a = {
        .mq_maxmsg = 10,
        .mq_msgsize = MSIZE
    };

    if ((q = mq_open(QNAME, O_CREAT | O_RDWR | O_NONBLOCK | O_EXCL, 0666, &a)) == -1) {
        PERROR_AND_WAIT("mq_open");
    }

    if (cpid > 0) {
        //parent: shares queue descriptor with child

        if (mq_unlink(QNAME) == -1) {
            PERROR_AND_WAIT("mq_unlink");
        }
        char msg[MSIZE +1];
        while (wpid == 0) {
            sleep(1);
            while ((n = mq_receive(q, msg, MSIZE, NULL)) > 0) {
                msg[n] = '\0';
                printf("Message: '%s'\n", msg);
            }

            if (n == -1 && errno != EAGAIN) {
                PERROR_AND_WAIT("mq_receive");
            } 

            if ((wpid = waitpid(cpid, NULL, WNOHANG)) == -1) {
                PERROR_AND_WAIT("waitpid");
            }
        }
        if (mq_close(q) == -1) {
            PERROR_AND_WAIT("mq_close");
        }
    } else 
    {
        if (mq_send(q, "Hello", sizeof("hello"), 1) == -1) {
            PERROR_AND_WAIT("mq_send");
        }
        sleep(2);

        if (mq_send(q, "Hello", sizeof("Queue"), 1) == -1) {
            PERROR_AND_WAIT("Queue");
        }
    }
}

gcc -lrt t09/posix_queue.c

POSIX Socket

Erlaubt Kommunikation über Netzwerk, oder über virtuelles LAN in einem System.

Verbindungsorientiert

Client

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define RECEIVE_TIMEOUT 0
#define SEND_TIMEOUT 0

int sockfd;

void connect_server (void)
{
	struct addrinfo hints, *server_info, *p;
	
	// alles auf 0 setzen
	memset(&hints, 0, sizeof(hints)); 
	hints.ai_familiy = AF_INET;
	hints.ai_socktype = SOCK_STREAM;

	// Hole Infos zu Socket auf
	// localhost und port 80
	int status = getaddrinfo("localhost", "80", &hints, &server_info);
	if (status != 0) {
	    perror("getaddrinfo konnte nicht ausgeführt werden");
	    exit(EXIT_FAILURE);
	}

	for (p = server_info; p != NULL; p = p->ai_next) {
		if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
		{
			perror ("socket error");
			exit(EXIT_FAILURE);
		}

		if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
			close(sockfd);
			perror ("Es konnte keine Verbinung hergestellt werden");
			exit(EXIT_FAILURE);
		}

		// wenn erfolgreich verbunden, aus der Schleife ausbrechen
		break;
   }

    if (p == NULL) {
        perror ("Es konnte keine Verbinung hergestellt werden");
		exit(EXIT_FAILURE);
    }


	// SOCKET VERBUNDEN
}

void write(char * buffer, int len)
{
	int totalSent = 0;

	while (totalSent < len) {
		int bytesSent = send(
			communicationSocket,
			buffer + totalSent,
			len - totalSent,
			SEND_TIMEOUT);

		if (bytesSent == -1) {
			perror ("Error in send");
			exit(EXIT_FAILURE);
		}
		totalSent += bytesSent;
   }
}

int read(char *buffer, int len)
{
	int totalReceived = 0;

	while (totalReceived < len) {
		int bytesReceived = recv(
			communicationSocket,
			buffer + totalReceived,
			len - totalReceived, 
			RECEIVE_TIMEOUT);
			
		if (bytesReceived == -1) {
			perror("Error in Receive");
			exit(EXIT_FAILURE);
		}

		totalReceived += bytesReceived;
	}

	return totalReceived;
}

void main (void)
{
	connect_server();

	write ("Hello World", 11);

	char bufffer[1000];
	int read = read(buffer, 1000);

	close(sockf);
}

Server

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <netdb.h>
#include <unistd.h>


#define RECEIVE_TIMEOUT 0
#define SEND_TIMEOUT 0
#define MAX_QUEUE 1

void server_init(){
   // BEGIN-STUDENTS-TO-ADD-CODE

   struct addrinfo hints, *server_info, *p;

   memset(&hints, 0, sizeof(hints));

   hints.ai_family = AF_INET;
   hints.ai_socktype = SOCK_STREAM;

   int status = getaddrinfo(NULL,"80" , &hints, &server_info);
   if(status != 0){
      ExitOnError(status, "getaddrinfo");
   }

   int sockfd;
   for (p = server_info; p != NULL; p = p->ai_next) {
      if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
         perror("socket error");
         exit(EXIT_FAILURE);
      }

      if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
         close(sockfd);
         perror("connect error");
         exit(EXIT_FAILURE);
      }

      break; // wenn erfolgreich verbunden, aus der Schleife ausbrechen
   }

   ListeningSocket = sockfd;

   listen(ListeningSocket, MAX_QUEUE);

   // END-STUDENTS-TO-ADD-CODE
}

int getRequest(char* requestBuffer, int max_len)
{
   // BEGIN-STUDENTS-TO-ADD-CODE
   int bytesReceived = recv(
      connectedSocket,
      requestBuffer,
      max_len -1,
      RECEIVE_TIMEOUT);
   requestBuffer[bytesReceived] = '\0';
   return bytesReceived;
   // END-STUDENTS-TO-ADD-CODE
}

void sendResponse(char* response, int resp_len)
{
   // BEGIN-STUDENTS-TO-ADD-CODE
   send(connectedSocket, response, resp_len, 0);
   // END-STUDENTS-TO-ADD-CODE
}

void server_close_connection(void)
{
   // BEGIN-STUDENTS-TO-ADD-CODE
   close(connectedSocket);
   // END-STUDENTS-TO-ADD-CODE
}


int wait_client(void) {
   connectedSocket = accept(ListeningSocket, 0, 0);
   return connectedSocket;
}

void main (void)
{
	server_init();
	wait_client();

	char buffer[1000];

	getRequest(buffer, 1000);
	sendResponse("Hello World", 11);
	server_close_connection();
}


Verbindungslos

Client/Server Architektur • Client

  • Socketdefinition (getaddrinfo(), socket())
  • keine Verbindungsanfrage
  • bidirektionales Lesen/Schreiben (recfrom(), sendto())
  • danach Socket schliessen (close()) • Server
  • Socketdefinition (getaddrinfo(), socket(), bind())
  • kein Abwarten und Akzeptieren von Verbindungsanfragen
  • bidirektionales Lesen/Schreiben (recfrom(), sendto())
  • danach eventuell Socket schliessen (close())

Sync / Semaphores und Signals werden verwendet um Zugriff auf geteilte Ressourcen zu koordinieren. Zudem können damit Threads aufeinander warten.

Syncs werden notwendig, wenn ein Thread auf einen anderen warten muss.

  • z.B. wenn eine Resource im einten geöffnet ist, dann darf der andere diese erst öffnen, sobald der erste durch ist.
  • Oder in einer kritischen Funktion, die IMMER ganz durchlaufen werden muss.
// Mit dem volatile keyword wird klargestellt,
// dass diese Resource von mehreren Threads verwendet wird.
volatile int iAmShared = 0;

Mutex - Mutual Exclusion

  • Resourcen werden gegenseitig ausgeschlossen, ein Task blockiert den Eintritt für andere Tasks in die Crititcal Section

Probleme

  • Runtime Kosten (locks dauern)
  • Rekursion
  • Kein Unlock (Bei early return)
  • Deadlock
#include <sys/types.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

const int N = 100000000;

// shared value
volatile int value = 0;

pthread_mutex_t mutex;

void * count(void * arg)
{
    int delta = *(int*) arg;

    for (int i = 0; i < N; i++)
    {
        if (pthread_mutex_lock(&mutex) != 0)
        {
            perror("Mutex konnte nicht besetzt werden");
            exit(EXIT_FAILURE);
        }

        // Gemeinsame Variable auslesen
        int a = value;

        // Modifiziern
        a += delta;

        // Variable wieder setzen
        value = a;


        // Mutex wieder freigeben
        if (pthread_mutex_unlock(&mutex) != 0)
        {
            perror("Mutex konnte nicht freigegeben werden");
            exit(EXIT_FAILURE);
        }
    }
    return NULL;
}

int main (void)
{
    if (pthread_mutex_init(&mutex, NULL) != 0)
    {
        perror("Mutex konnte nicht initialisiert werden");
        exit(EXIT_FAILURE);
    }

    pthread_t th_inc;
    pthread_t th_dec;


    int inc = +1;
    int dec = -1;
    if (pthread_create(&th_inc, NULL, count, &inc) != 0)
    {
        perror("Thread konnte nicht erstellt werden");
        exit(EXIT_FAILURE);
    }
    
    if (pthread_create(&th_dec, NULL, count, &dec) != 0)
    {
        perror("Thread konnte nicht erstellt werden");
        exit(EXIT_FAILURE);
    }

    // noch auf threads waretn
    if (pthread_join(th_inc, NULL) != 0)
    {
        perror("Thread konnte nicht beendet werden");
        exit(EXIT_FAILURE);
    }
    if (pthread_join(th_dec, NULL) != 0)
    {
        perror("Thread konnte nicht beendet werden");
        exit(EXIT_FAILURE);
    }

}
gcc -pthread t10/critical_section.c

Semaphore

Tasks können über Semaphoren Synchronisationspunkte vereinbaren

Probleme

  • Synchronisation kostet Zeit
  • Wenn ein wait zu viel, bzwh. ein signal zu wenig
  • Deadlocks, System kann komplett blockiert werden
// C program to demonstrate working of Semaphores
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

sem_t mutex;

void* thread(void* arg)
{
	//wait
	sem_wait(&mutex);
	printf("\nEntered..\n");

	//critical section
	sleep(4);
	
	//signal
	printf("\nJust Exiting...\n");
	sem_post(&mutex);
}


int main()
{

	// 1 -> Initial value
	// unbedingt auf das achten
	sem_init(&mutex, 0, 1);
	pthread_t t1,t2;
	pthread_create(&t1,NULL,thread,NULL);
	sleep(2);
	pthread_create(&t2,NULL,thread,NULL);
	pthread_join(t1,NULL);
	pthread_join(t2,NULL);
	sem_destroy(&mutex);
	return 0;
}

gcc -pthread t10/semaphore.c

KT

Anforderungsanalye

Domänenmodell

Softwarearchitektur

Cheat Sheet UML

Anforderungsanalyse

## Usability und User Experience (UX)

Alt text

Usability

Wie einfach kann eine SW-Applikation benutzt werden.

Aspekte:

  • Benutzer
  • Seine Ziele / Aufgaben
  • Sine Kontext
  • Softwaresystem (inkl. UI)

### Effektivität

  • Der Benutzer kann all seine Aufgaben vollständig erfüllen
  • Mit der gewünschten Genauigkeit

Effizienz

  • Der Benutzer kann seine Aufgaben mit minimalem / angemessemen Aufwand erledigen

    • Mental
    • Physisch
    • Zeit

Zufriedenheit

  • Mit dem System / der Interaktion

    • Minimum: Benutzer ist nicht verärgert
    • Normal: Benutzer ist zufrieden
    • Optimal: Benutzer ist erfreut

Kontextszenario

Das Kontextszenario beschreibt, wie das Produkt effektiv verwendet wird. Es werden keine technischen Anforderungen beschrieben.

Persona

Das Persona beschreibt die Person, die die Anwendung verwendet. Dies besteht aus:

  • Name
  • Wer diese Person ist, und was sie macht
  • Was sie sich vom Produkt erhofft

Szenario

Das Szenario beschreibt, wie die Persona das Produkt verwendet. Dabei wird die Verwendung verallgemeinert und Absätze beschreiben die Prozesse. Der Aufbau ist wie folgt:

  • Vorbedingungen, was also gegeben sein muss.
  • Wie der Anwender das Produkt verwendet, aufgeteilt in einzelne Abschnitte. Auch Interaktionen mit externen Anwendersystemen werden aufgezeigt.

Use Cases

Ein Use Case beschreibt einen einzelnen Prozess des Kontextszenarios. Im Gegensatz zu Scrum, hat ein Use Case nichts mit der Technik zu tun, sonder beschreibt jediglich die funktionalen Anforderungen (also was der Anwender sieht).

Anwendungsfalldiagramm (Use-Case Diagramm)

Beschreibt, wer (Systeme & Personas) mit den Anwendungsfällen interagiert. Jeder Use Case muss mit einer Nummer eindeutig identifizierbar sein.

# Fully dressed Use Case

  • Name des Use Cases und Use Case Nummer zwecks eindeutiger Identifizierung
  • Akteure
  • Auslöser
  • Kurzbeschreibung
  • Beschreibung der essentiellen Schritte als Standardablauf
  • Beschreibung von alternativen Abfolgen
  • Vorbedingungen und Nachbedingungen
  • Systemgrenzen

Das Domänenmodell dient als Überblick der gesamten Anwendung. Dabei werden die Teile des Systems in unterschiedliche Blöcke getrennt. Diese Blöcke besitzen Attribute, die IMMER einen Bezug zur Welt des Anwenders besitzen, also keine ids, etc.

Die Verbindungen zwischen diesen Blöcken sind beschreibend, also Verben. Das Modell wird als UML Klassendiagramm in einer vereinfachten Form gezeichnet.

Beziehungen

Komposition

Ein Objekt kann ohne das andere (Parent) nicht existieren. Wird das Parent gelöscht, so stirbt auch das Kind. Ein Parent kann mehrere Kinder besitzen. (1 -> *)

Aggregation

Es besteht eine hat Beziehung. Ein Parent kann mehrere Kinder besitzen (1 -> *)

Generalisierung / Spezialisierung

Eine Zusammenhang wird genauer definiert. Somit hat ein Typ nur noch die Funktionen, die NUR ihm zugeordnet werden können. Die isA Regel, sowie die 100% Regel darf nicht verletzt werden.

isA: Ist ein 100%: Eine Spezialisierung darf nicht etwas anderes können, als die Kasse selber es könnte.

Vorgehen

Die Vorgehensweise eines Kartografens wird angewendet, also zuerst das Grobe bestimmen / identifizieren und danach mit weiteren Details ausschmücken. Zudem wird unwichtiges weggelassen. Es wird NUR vorhandenes hinzugefügt.

  1. Konzepte identifizieren
  2. Konzepte mit Attributen versehen
  3. Konzepte in Verbindung zueinander setzen

Attribute

Datentypen

Die meisten Attributtypen sind einfach (primitiv)

  • Integer, float, boolean
  • Werden im DB normalerweise nicht angegeben

Konzepte

Sind in einer Klasse Attribute vorhanden, die normalerweise zu einem eigenen Typen gehören, oder sind diese mehrfach verwendet, so werden diese in Konzepte ausgelagert.

Anwendung:

  • Der Type besteht aus mehreren Abschnitten (Adresse)
  • Operationen darauf möglich sind, z.B. Validierung einer Kreditkartennummer
  • Der Typ selber noch Attribute hat, z.B. ein Verkaufspreis der ein Anfangs- und Enddatum hat
  • Der Typ mit einer Einheit verknüpft ist, z.B. ein Preis mit einer Währung

Anti-Pattern

  • Assoziationen anstelle von Attributen verwenden um Konzepte in Beziehung zueinander zu setzen.
  • Keine Software Klassen im Domänenmodell, die es nicht in der Fachdomäne gibt. z.B. print(), toString(), …

Beschreibungsklasse

Attribute, die für alle Artikel eines Typs gleich sind, werden in eine eigene Klasse herausgezogen.

Logische Architektur

Auftrennung in Module

ISO 25010

Diese ISO Norm beschreibt, wie eine Software zu analysieren ist. Functional Suitability

  • Appropriateness
  • Accuracy
  • Compliance

Reliability

  • Availability
  • Fault tolerance
  • Recoverability
  • Compliance

Performance efficiency

  • Timebehaviour
  • Resourceutilisation
  • Compliance

Operation (Useability)

  • Appropriatenessrecogniseability
  • Leranability
  • Ease-of-use
  • Helpfulness
  • Attractiveness
  • Technicalaccessibility
  • Compliance

Security

  • Confidentiality
  • Integrity
  • Non-repudiation
  • Accountability
  • Authenticity
  • Compliance

Maintainability

  • Modularity
  • Reusability
  • Analyzability
  • Changeability
  • Modification stability
  • Testability
  • Compliance

Transferability

  • Portability
  • Adaptability
  • Installability
  • Compliance

Schnittstellen (Interfaces)

Exportierte Schnittstellen

  • Definieren angebotene Funktionalität
  • Sind im Sinne eines Vertrags garantiert
  • Definieren wie ein Modul verwendet werden muss
  • Modul kann intern beliebig verändert werden, solange Schnittstellen gleich bleiben

Importierte Schnittstellen

  • Verwendet ein Modul andere Module, so importiert sie deren Schnittstellen
  • Einzige Kopplung zwischen Modulen

Kapselung und Austaschbarkeit

  • Der Bausteil kapselt die Implementierung dieser Schnittstellen ab. Somit ist die Implementation unsichtbar für die Aussenwelt
  • Daher kann dieser durch andere Bausteine problemlos ersetzt werden

Güte einer Modularisierung

Kohäsion

  • Ein Mass für die Stärke des inneren Zusamenhangs
  • Je höher die Kohäsion innerhalb eines Moduls, desto besser die Modularisierung
    • schlecht: zufällig, zeitlich
    • gut: funktional, objektbezogen

Kopplung

  • Ein Mass für die Abhängigket zwischen Modulen
  • Je geringer die wechselseitige Kopplung zwischen den Modulen, desto besser die Modularisierung
    • schlechte: Globale Kopplung (Globale Daten)
    • akzeptabel: Datenbereichskopplung: (Referenz auf gemeinsame Daten)
    • gut: Datenkopplung (alle Daten werden beim Aufruf der Schnittstelle übergeben)

Clean Architecture

Entities

Kapseln die Business Rules gültig für das gesamte Unternehmen

Use Cases

Beinhalten die Business Rules einer Anwendung, orchestriert die Verwendung der Entities

Interface Adapters

Adapter für die Konvertierung von Daten aus Datenbank oder web

Frameworks and Drivers

![[Pasted image 20230602171312.png]]

N+1 View Model

Logical View

  • Welche Funktionalität bietet das System gegen aussen an?
  • Wichtige Aspekte: Schichten, Subsysteme, Pakete, Frameworks, Klassen, Interfaces
  • UML: Systemsequenzdiagramme, Interaktionsdiagramme, Klassendiagramm, Zustandsdiagramme

Process View

  • Welche Prozesse laufen wo und wie ab im System?
  • Wichtige Aspekte: Prozesse, Threads, Wie werden Anforderungen wie Performance und Stabilität erreicht?
  • UML: Klassendiagramme, Interaktionsdiagramme, Aktivitätsdiagramme

Development View (Implementation View):

  • Wie wurde die logische Struktur (Layer, Schichten, Komponenten) umgesetzt?
  • Wichtige Aspekte: Source Code, Executables, Artefakte
  • UML: Paketdiagramme, Komponentendiagramme

Physical View (Deployment View):

  • Auf welcher Infrastruktur wird ein System ausgeliefert/betrieben?
  • Wichtige Aspekte: Prozessknoten, Netzwerke, Protokolle
  • UML: Deployment Diagram

Scenarios (Use Cases)

  • Welches sind die wichtigsten Use-Cases und ihre nichtfunktionalen Anforderungen? Wie wurden sie umgesetzt?
  • Wichtige Aspekte: Architektonisch wichtige Ucs, deren nichtfunktionale Anforderungen und deren Implementation
  • UML: UC-Diagramm, Systemsequenzdiagramme, UC- Realisierungen

Paketdiagramme

Design Patterns

Creational Patterns

Abstract Factory

Creating multiple objects with variations.

Alt text

public interface Shape {
   void draw();
}

public class RoundedRectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside RoundedRectangle::draw() method.");
   }
}

public class RoundedSquare implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside RoundedSquare::draw() method.");
   }
}

public class Rectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

// Abstract factory
public abstract class AbstractFactory {
   abstract Shape getShape(String shapeType) ;
}

public class ShapeFactory extends AbstractFactory {
   @Override
   public Shape getShape(String shapeType){    
      if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();         
      }else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }	 
      return null;
   }
}

public class RoundedShapeFactory extends AbstractFactory {
   @Override
   public Shape getShape(String shapeType){    
      if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new RoundedRectangle();         
      }else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new RoundedSquare();
      }	 
      return null;
   }
}

Builder

Objekte werden Schritt für Schritt zusammengebaut. Der Konstruktor baut die "Grundstruktur" und für jeden weiteren Schritt ist eine Methode vorhanden.

Alt text

public class Computer {
	
	//required parameters
	private String HDD;
	private String RAM;
	
	//optional parameters
	private boolean isGraphicsCardEnabled;
	private boolean isBluetoothEnabled;
	

	public String getHDD() {
		return HDD;
	}

	public String getRAM() {
		return RAM;
	}

	public boolean isGraphicsCardEnabled() {
		return isGraphicsCardEnabled;
	}

	public boolean isBluetoothEnabled() {
		return isBluetoothEnabled;
	}
	
	private Computer(ComputerBuilder builder) {
		this.HDD=builder.HDD;
		this.RAM=builder.RAM;
		this.isGraphicsCardEnabled=builder.isGraphicsCardEnabled;
		this.isBluetoothEnabled=builder.isBluetoothEnabled;
	}
	
	//Builder Class
	public static class ComputerBuilder{

		// required parameters
		private String HDD;
		private String RAM;

		// optional parameters
		private boolean isGraphicsCardEnabled;
		private boolean isBluetoothEnabled;
		
		public ComputerBuilder(String hdd, String ram){
			this.HDD=hdd;
			this.RAM=ram;
		}

		public ComputerBuilder setGraphicsCardEnabled(boolean isGraphicsCardEnabled) {
			this.isGraphicsCardEnabled = isGraphicsCardEnabled;
			return this;
		}

		public ComputerBuilder setBluetoothEnabled(boolean isBluetoothEnabled) {
			this.isBluetoothEnabled = isBluetoothEnabled;
			return this;
		}
		
		public Computer build(){
			return new Computer(this);
		}

	}

}

Factory Method

Ein Interface wird verwendet, um etwas zu beschreiben. Dieses wird von unterschiedlichen Implementationen implementiert.

public interface Button {
    void render();
    void onClick();
}

public class HtmlButton implements Button {

    public void render() {
        System.out.println("<button>Test Button</button>");
        onClick();
    }

    public void onClick() {
        System.out.println("Click! Button says - 'Hello World!'");
    }
}

public class OtherButton implements Button {

    public void render() {
        ...
        onClick();
    }

    public void onClick() {
        ...
    }
}


Button x = new HtmlButton();

Prototype

Wird verwendet, wenn Objekte exakt geklonnt werden sollen. Dabei werden nicht die einzelnen Felder kopiert, sondern das Klonen der Klasse überlassen.

Alt text

public abstract class Tree {
    
    // ...
    public abstract Tree copy();
}

public class PlasticTree extends Tree {

    // ...

    @Override
    public Tree copy() {
        PlasticTree plasticTreeClone = new PlasticTree(this.getMass(), this.getHeight());
        plasticTreeClone.setPosition(this.getPosition());
        return plasticTreeClone;
    }

}

public class PineTree extends Tree {
    // ...

    @Override
    public Tree copy() {
        PineTree pineTreeClone = new PineTree(this.getMass(), this.getHeight());
        pineTreeClone.setPosition(this.getPosition());
        return pineTreeClone;
    }
}

Singleton

Wird verwendet, um von einer Klasse genau eine Instanz zu haben. Wird verwendet um Resourcen, z.B. Zugang zu einer Datenbank, Datei in genau einer Instanz zu halten.

public final class ClassSingleton {

    private static ClassSingleton INSTANCE;
    private String info = "Initial info class";
    
    private ClassSingleton() {        
    }
    
    public static ClassSingleton getInstance() {
        if(INSTANCE == null) {
            INSTANCE = new ClassSingleton();
        }
        
        return INSTANCE;
    }

    // getters and setters
}

Structural Patterns

Adapter

Das Adapter Pattern erlaubt es, Anwendungen die mit unterschiedlichen Protokollen / Schnittstellen arbeiten, miteinander zu verknüpfen.

Alt text


public class Volt {

	private int volts;
	
	public Volt(int v){
		this.volts=v;
	}

	public int getVolts() {
		return volts;
	}

	public void setVolts(int volts) {
		this.volts = volts;
	}
	
}

public class Socket {

	public Volt getVolt(){
		return new Volt(120);
	}
}

public interface SocketAdapter {

	public Volt get120Volt();
		
	public Volt get12Volt();
	
	public Volt get3Volt();
}


Bridge

Eine Klasse wird aus unterschiedlichen anderen Klassen zusammengesetzt

Alt text

public interface Color {

	public void applyColor();
}

public abstract class Shape {
	//Composition - implementor
	protected Color color;
	
	//constructor with implementor as input argument
	public Shape(Color c){
		this.color=c;
	}
	
	abstract public void applyColor();
}

public class Triangle extends Shape{

	public Triangle(Color c) {
		super(c);
	}

	@Override
	public void applyColor() {
		System.out.print("Triangle filled with color ");
		color.applyColor();
	} 

Composite

Macht nur Sinn, wenn die Anwendung als Baum aufgebaut ist.

Alt text

public interface Department {
    void printDepartmentName();
}

// LEAFS
public class FinancialDepartment implements Department {

    private Integer id;
    private String name;

    public void printDepartmentName() {
        System.out.println(getClass().getSimpleName());
    }

    // standard constructor, getters, setters
}

public class SalesDepartment implements Department {

    private Integer id;
    private String name;

    public void printDepartmentName() {
        System.out.println(getClass().getSimpleName());
    }

    // standard constructor, getters, setters
}

// COMPOSITE
public class HeadDepartment implements Department {
    private Integer id;
    private String name;

    private List<Department> childDepartments;

    public HeadDepartment(Integer id, String name) {
        this.id = id;
        this.name = name;
        this.childDepartments = new ArrayList<>();
    }

    public void printDepartmentName() {
        childDepartments.forEach(Department::printDepartmentName);
    }

    public void addDepartment(Department department) {
        childDepartments.add(department);
    }

    public void removeDepartment(Department department) {
        childDepartments.remove(department);
    }
}

Decorator

Erlaubt es neues Verhalten zu Objeken hinzuzufügen.

Alt text

public interface ChristmasTree {
    String decorate();
}
public class ChristmasTreeImpl implements ChristmasTree {

    @Override
    public String decorate() {
        return "Christmas tree";
    }
}

public abstract class TreeDecorator implements ChristmasTree {
    private ChristmasTree tree;
    
    // standard constructors
    @Override
    public String decorate() {
        return tree.decorate();
    }
}

public class BubbleLights extends TreeDecorator {

    public BubbleLights(ChristmasTree tree) {
        super(tree);
    }
    
    public String decorate() {
        return super.decorate() + decorateWithBubbleLights();
    }
    
    private String decorateWithBubbleLights() {
        return " with Bubble Lights";
    }
}

Facade

Bietet ein vereinfachtes Interface für eine Bibliothek, Framework oder Set an komplexen Klassen.

Alt text


public interface Shape {
   void draw();
}
public class Rectangle implements Shape {

   @Override
   public void draw() {
      System.out.println("Rectangle::draw()");
   }
}
public class Square implements Shape {

   @Override
   public void draw() {
      System.out.println("Square::draw()");
   }
}
public class Circle implements Shape {

   @Override
   public void draw() {
      System.out.println("Circle::draw()");
   }
}

// FACADE
public class ShapeMaker {
   private Shape circle;
   private Shape rectangle;
   private Shape square;

   public ShapeMaker() {
      circle = new Circle();
      rectangle = new Rectangle();
      square = new Square();
   }

   public void drawCircle(){
      circle.draw();
   }
   public void drawRectangle(){
      rectangle.draw();
   }
   public void drawSquare(){
      square.draw();
   }
}

Flyweight

Erlaubt es eine grössere Anzahl an Objekten in die gleiche Grösse an Arbeitsspeicher zu packen. Es werden gemeinsame Daten in einer gemeinsamen Tabelle gespeichert.

public class Tree {
    private int x;
    private int y;
    private TreeType type;

    public Tree(int x, int y, TreeType type) {
        this.x = x;
        this.y = y;
        this.type = type;
    }

    public void draw(Graphics g) {
        type.draw(g, x, y);
    }
}

public class TreeType {
    private String name;
    private Color color;
    private String otherTreeData;

    public TreeType(String name, Color color, String otherTreeData) {
        this.name = name;
        this.color = color;
        this.otherTreeData = otherTreeData;
    }

    public void draw(Graphics g, int x, int y) {
        g.setColor(Color.BLACK);
        g.fillRect(x - 1, y, 3, 5);
        g.setColor(color);
        g.fillOval(x - 5, y - 10, 10, 10);
    }
}

public class TreeFactory {
    static Map<String, TreeType> treeTypes = new HashMap<>();

    public static TreeType getTreeType(String name, Color color, String otherTreeData) {
        TreeType result = treeTypes.get(name);
        if (result == null) {
            result = new TreeType(name, color, otherTreeData);
            treeTypes.put(name, result);
        }
        return result;
    }
}

public class Forest {
    private List<Tree> trees = new ArrayList<>();

    public void plantTree(int x, int y, String name, Color color, String otherTreeData) {
        TreeType type = TreeFactory.getTreeType(name, color, otherTreeData);
        Tree tree = new Tree(x, y, type);
        trees.add(tree);
    }

    @Override
    public void paint(Graphics graphics) {
        for (Tree tree : trees) {
            tree.draw(graphics);
        }
    }
}

Proxy

Übertragt die Steuerung eines Objektes in eine vorgelagertes Stellvertreterobjekt. Das Proxy dient als Schnittstelle zu seinem Subjekt.

Alt text

public interface ExpensiveObject {
    void process();
}
public class ExpensiveObjectImpl implements ExpensiveObject {

    public ExpensiveObjectImpl() {
        heavyInitialConfiguration();
    }
    
    @Override
    public void process() {
        LOG.info("processing complete.");
    }
    
    private void heavyInitialConfiguration() {
        LOG.info("Loading initial configuration...");
    }
    
}
public class ExpensiveObjectProxy implements ExpensiveObject {
    private static ExpensiveObject object;

    @Override
    public void process() {
        if (object == null) {
            object = new ExpensiveObjectImpl();
        }
        object.process();
    }
}
public static void main(String[] args) {
    ExpensiveObject object = new ExpensiveObjectProxy();
    object.process();
    object.process();
}
Proxy Type BeschreibungVorteileNachteile
Virtual ProxyObjekt wird erst geladen, wenn benötigt (Lazy loading) Spart Arbeitsspeicher 
Remot Proxy Originales Objekt ist nicht im aktuellen Speicher präsent
Protection Proxy Erlaubt es eine zusätzliche Sicherheitsschicht einem Objekt hinzuzufügen Erlaubt Zugriffsrechte

Behaviroal Patterns

Chain of Responsibility

Anfragen werden entlang einer Kette weitergeleitet.

public abstract class AbstractLogger {
   public static int INFO = 1;
   public static int DEBUG = 2;
   public static int ERROR = 3;

   protected int level;

   //next element in chain or responsibility
   protected AbstractLogger nextLogger;

   public void setNextLogger(AbstractLogger nextLogger){
      this.nextLogger = nextLogger;
   }

   public void logMessage(int level, String message){
      if(this.level <= level){
         write(message);
      }
      if(nextLogger !=null){
         nextLogger.logMessage(level, message);
      }
   }

   abstract protected void write(String message);
	
}
public class ConsoleLogger extends AbstractLogger {

   public ConsoleLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {		
      System.out.println("Standard Console::Logger: " + message);
   }
}
public class ErrorLogger extends AbstractLogger {

   public ErrorLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {		
      System.out.println("Error Console::Logger: " + message);
   }
}
public class FileLogger extends AbstractLogger {

   public FileLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {		
      System.out.println("File::Logger: " + message);
   }
}

// DEMO
public class ChainPatternDemo {
	
   private static AbstractLogger getChainOfLoggers(){

      AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
      AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
      AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

      errorLogger.setNextLogger(fileLogger);
      fileLogger.setNextLogger(consoleLogger);

      return errorLogger;	
   }

   public static void main(String[] args) {
      AbstractLogger loggerChain = getChainOfLoggers();

      loggerChain.logMessage(AbstractLogger.INFO, 
         "This is an information.");

      loggerChain.logMessage(AbstractLogger.DEBUG, 
         "This is an debug level information.");

      loggerChain.logMessage(AbstractLogger.ERROR, 
         "This is an error information.");
   }
}

Command

Daten die zum Ausführen einer Aktion benötigt werden, werden in einer eigenen Klasse gespeichert / ausgeführt.

Diese Aktionen werden werden von einem Empfänger erhalten und ausgeführt.

@FunctionalInterface
public interface TextFileOperation {
    String execute();
}

public class OpenTextFileOperation implements TextFileOperation {

    private TextFile textFile;
    
    // constructors
    
    @Override
    public String execute() {
        return textFile.open();
    }
}

public class SaveTextFileOperation implements TextFileOperation {
    
    // same field and constructor as above
        
    @Override
    public String execute() {
        return textFile.save();
    }
}

// RECEIVER
public class TextFile {
    
    private String name;
    
    // constructor
    
    public String open() {
        return "Opening file " + name;
    }
    
    public String save() {  
        return "Saving file " + name;
    }
    
    // additional text file methods (editing, writing, copying, pasting)
}


//SENDER
public class TextFileOperationExecutor {
    
    private final List<TextFileOperation> textFileOperations
     = new ArrayList<>();
    
    public String executeOperation(TextFileOperation textFileOperation) {
        textFileOperations.add(textFileOperation);
        return textFileOperation.execute();
    }
}

// VERWENDUNG
public static void main(String[] args) {
    TextFileOperationExecutor textFileOperationExecutor
      = new TextFileOperationExecutor();
    textFileOperationExecutor.executeOperation(
      new OpenTextFileOperation(new TextFile("file1.txt"))));
    textFileOperationExecutor.executeOperation(
      new SaveTextFileOperation(new TextFile("file2.txt"))));
}

Interpreter

Definiert eine Repräsentation für die Grammatik einer Sprache und die Möglichkeit, Sätze dieser Sprache zu interpretieren.

Alt text



class Select implements Expression {

    private String column;
    private From from;

    // constructor

    @Override
    public List<String> interpret(Context ctx) {
        ctx.setColumn(column);
        return from.interpret(ctx);
    }
}

class From implements Expression {

    private String table;
    private Where where;

    // constructors

    @Override
    public List<String> interpret(Context ctx) {
        ctx.setTable(table);
        if (where == null) {
            return ctx.search();
        }
        return where.interpret(ctx);
    }
}

class Where implements Expression {

    private Predicate<String> filter;

    // constructor

    @Override
    public List<String> interpret(Context ctx) {
        ctx.setFilter(filter);
        return ctx.search();
    }
}

class Context {

    private static Map<String, List<Row>> tables = new HashMap<>();

    static {
        List<Row> list = new ArrayList<>();
        list.add(new Row("John", "Doe"));
        list.add(new Row("Jan", "Kowalski"));
        list.add(new Row("Dominic", "Doom"));

        tables.put("people", list);
    }

    private String table;
    private String column;
    private Predicate<String> whereFilter;

    // ... 

    List<String> search() {

        List<String> result = tables.entrySet()
          .stream()
          .filter(entry -> entry.getKey().equalsIgnoreCase(table))
          .flatMap(entry -> Stream.of(entry.getValue()))
          .flatMap(Collection::stream)
          .map(Row::toString)
          .flatMap(columnMapper)
          .filter(whereFilter)
          .collect(Collectors.toList());

        clear();

        return result;
    }
}

Iterator

Erlaubt es Datenstrukturen jeglicher Art zu durchlaufen.

public interface Iterator {
   public boolean hasNext();
   public Object next();
}

public interface Container {
   public Iterator getIterator();
}

public class NameRepository implements Container {
   public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};

   @Override
   public Iterator getIterator() {
      return new NameIterator();
   }

   private class NameIterator implements Iterator {

      int index;

      @Override
      public boolean hasNext() {
      
         if(index < names.length){
            return true;
         }
         return false;
      }

      @Override
      public Object next() {
      
         if(this.hasNext()){
            return names[index++];
         }
         return null;
      }		
   }
}

public class IteratorPatternDemo {
	
   public static void main(String[] args) {
      NameRepository namesRepository = new NameRepository();

      for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
         String name = (String)iter.next();
         System.out.println("Name : " + name);
      } 	
   }
}

Mediator

Wird benötigt, um einen "Kabelsalat" an Dependencies durch einen zentralen Zugang zu steuern.

Alt text

public class Button {
    private Fan fan;

    // constructor, getters and setters

    public void press(){
        if(fan.isOn()){
            fan.turnOff();
        } else {
            fan.turnOn();
        }
    }
}

public class Fan {
    private Button button;
    private PowerSupplier powerSupplier;
    private boolean isOn = false;

    // constructor, getters and setters

    public void turnOn() {
        powerSupplier.turnOn();
        isOn = true;
    }

    public void turnOff() {
        isOn = false;
        powerSupplier.turnOff();
    }
}

public class PowerSupplier {
    public void turnOn() {
        // implementation
    }

    public void turnOff() {
        // implementation
    }
}

Umwandeln in

public class Button {
    private Mediator mediator;

    // constructor, getters and setters

    public void press() {
        mediator.press();
    }
}

public class Fan {
    private Mediator mediator;
    private boolean isOn = false;

    // constructor, getters and setters

    public void turnOn() {
        mediator.start();
        isOn = true;
    }

    public void turnOff() {
        isOn = false;
        mediator.stop();
    }
}

public class Mediator {
    private Button button;
    private Fan fan;
    private PowerSupplier powerSupplier;

    // constructor, getters and setters

    public void press() {
        if (fan.isOn()) {
            fan.turnOff();
        } else {
            fan.turnOn();
        }
    }

    public void start() {
        powerSupplier.turnOn();
    }

    public void stop() {
        powerSupplier.turnOff();
    }
}

Memento

Erlaubt es Zustände zu speichern und wiederherzustellen.

Alt text

public class Memento {
   private String state;

   public Memento(String state){
      this.state = state;
   }

   public String getState(){
      return state;
   }	
}

public class Originator {
   private String state;

   public void setState(String state){
      this.state = state;
   }

   public String getState(){
      return state;
   }

   public Memento saveStateToMemento(){
      return new Memento(state);
   }

   public void getStateFromMemento(Memento memento){
      state = memento.getState();
   }
}

public class CareTaker {
   private List<Memento> mementoList = new ArrayList<Memento>();

   public void add(Memento state){
      mementoList.add(state);
   }

   public Memento get(int index){
      return mementoList.get(index);
   }
}

public class MementoPatternDemo {
   public static void main(String[] args) {
   
      Originator originator = new Originator();
      CareTaker careTaker = new CareTaker();
      
      originator.setState("State #1");
      originator.setState("State #2");
      careTaker.add(originator.saveStateToMemento());
      
      originator.setState("State #3");
      careTaker.add(originator.saveStateToMemento());
      
      originator.setState("State #4");
      System.out.println("Current State: " + originator.getState());		
      
      originator.getStateFromMemento(careTaker.get(0));
      System.out.println("First saved State: " + originator.getState());
      originator.getStateFromMemento(careTaker.get(1));
      System.out.println("Second saved State: " + originator.getState());
   }
}

### Observer

Definiert ein Mechanismus um mehrere Objekte zu informieren, wenn sich ein Objekt ändert.

Alt text

public class Subject {
	
   private List<Observer> observers = new ArrayList<Observer>();
   private int state;

   public int getState() {
      return state;
   }

   public void setState(int state) {
      this.state = state;
      notifyAllObservers();
   }

   public void attach(Observer observer){
      observers.add(observer);		
   }

   public void notifyAllObservers(){
      for (Observer observer : observers) {
         observer.update();
      }
   } 	
}

public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}

public class BinaryObserver extends Observer{

   public BinaryObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
      System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) ); 
   }
}

public class ObserverPatternDemo {
   public static void main(String[] args) {
      Subject subject = new Subject();

      new BinaryObserver(subject);

      System.out.println("First state change: 15");	
      subject.setState(15);
      System.out.println("Second state change: 10");	
      subject.setState(10);
   }
}

State

Ein Objekt ändert sein Verhalten bestimmend nach seinen internen Zuständen.

Alt text


public class Package {

    private PackageState state = new OrderedState();

    // getter, setter

    public void previousState() {
        state.prev(this);
    }

    public void nextState() {
        state.next(this);
    }

    public void printStatus() {
        state.printStatus();
    }
}

public interface PackageState {

    void next(Package pkg);
    void prev(Package pkg);
    void printStatus();
}

public class OrderedState implements PackageState {

    @Override
    public void next(Package pkg) {
        pkg.setState(new DeliveredState());
    }

    @Override
    public void prev(Package pkg) {
        System.out.println("The package is in its root state.");
    }

    @Override
    public void printStatus() {
        System.out.println("Package ordered, not delivered to the office yet.");
    }
}
public class DeliveredState implements PackageState {

    @Override
    public void next(Package pkg) {
        pkg.setState(new ReceivedState());
    }

    @Override
    public void prev(Package pkg) {
        pkg.setState(new OrderedState());
    }

    @Override
    public void printStatus() {
        System.out.println("Package delivered to post office, not received yet.");
    }
}

public class ReceivedState implements PackageState {

    @Override
    public void next(Package pkg) {
        System.out.println("This package is already received by a client.");
    }

    @Override
    public void prev(Package pkg) {
        pkg.setState(new DeliveredState());
    }
}

Strategy

Mehrere Algorithmen werden in separaten Klassen implementiert. Je nach Anwendung, kann zwischen diesen gewechselt werden.

Alt text

public interface Strategy {
   public int doOperation(int num1, int num2);
}

public class OperationAdd implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 + num2;
   }
}

public class OperationSubstract implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 - num2;
   }
}

public class OperationMultiply implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 * num2;
   }
}

public class Context {
   private Strategy strategy;

   public Context(Strategy strategy){
      this.strategy = strategy;
   }

   public int executeStrategy(int num1, int num2){
      return strategy.doOperation(num1, num2);
   }
}

public class StrategyPatternDemo {
   public static void main(String[] args) {
      Context context = new Context(new OperationAdd());		
      System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

      context = new Context(new OperationSubstract());		
      System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

      context = new Context(new OperationMultiply());		
      System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
   }
}

Template Method

Das Skelet von Algorithmen wird in der Superklasse definiert. Subklassen implementieren die offen gelegten Methoden der Superklasse.

Alt text

public abstract class Game {
   abstract void initialize();
   abstract void startPlay();
   abstract void endPlay();

   //template method
   public final void play(){

      //initialize the game
      initialize();

      //start game
      startPlay();

      //end game
      endPlay();
   }
}

public class Cricket extends Game {

   @Override
   void endPlay() {
      System.out.println("Cricket Game Finished!");
   }

   @Override
   void initialize() {
      System.out.println("Cricket Game Initialized! Start playing.");
   }

   @Override
   void startPlay() {
      System.out.println("Cricket Game Started. Enjoy the game!");
   }
}

public class Football extends Game {

   @Override
   void endPlay() {
      System.out.println("Football Game Finished!");
   }

   @Override
   void initialize() {
      System.out.println("Football Game Initialized! Start playing.");
   }

   @Override
   void startPlay() {
      System.out.println("Football Game Started. Enjoy the game!");
   }
}

public class TemplatePatternDemo {
   public static void main(String[] args) {

      Game game = new Cricket();
      game.play();
      System.out.println();
      game = new Football();
      game.play();		
   }
}

Visitor

Algorithmen werden vom Objekt getrennt, auf dem sie operieren.

Alt text

public class Document extends Element {

    List<Element> elements = new ArrayList<>();

    // ...

    @Override
    public void accept(Visitor v) {
        for (Element e : this.elements) {
            e.accept(v);
        }
    }
}

public class JsonElement extends Element {

    // ...

    public void accept(Visitor v) {
        v.visit(this);
    }
}

public class ElementVisitor implements Visitor {

    @Override
    public void visit(XmlElement xe) {
        System.out.println(
          "processing an XML element with uuid: " + xe.uuid);
    }

    @Override
    public void visit(JsonElement je) {
        System.out.println(
          "processing a JSON element with uuid: " + je.uuid);
    }
}

Auslosetool

Einflussfaktoren

Usability

  • Graphische Benutzeroberfläche
  • Presentation Layer

Zuverlässigkeit

  • Schnittstellen der Schichten verstänldihc und testbar gestalten
  • Verfikation der Import Datei auf Korrektheit (Ausgabe einer verständlichen Fehlermeldung)

Design Vorgaben

  • Trennung der UI-Logik
  • Schichten Architektur

Supportability

  • Wiederverwendbarkeit
  • Modularisierung
  • Support von verschiedenen Import-Dateiformaten (Abstraktionsschicht)

Logische Architektur

Switfs 4 Restaurants

  • Besteht aus Mobilgeräten für das Servierpersonal, mit dem sie Bestellungn aufnahmen können sowie Bezahlungen abwickeln können.
  • Zentralkassensystem für die Theke
  • Touchscreen für die Küche, wo die bestellten Waren erscheinen

Stakeholder Map

Domänenmodell

Jede Servierperson besitzt ein Mobilgerät, auf dem sie Ihre Bestellungen erfasst. Die Menus werden direkt auf das Display in der Küche geschickt, die sie zubereitet. Die Getränkebestellungen werden auf dem Zentralcomputer an der Theke angezeigt. Eine Servierperson kann eine laufende Bestellung von einer anderen Servierperson übernehmen, indem sie einfach die entsprechende Tischnummer eingibt auf dem Mobilgerät.

Diskussionsforum

Design-Klassendiagram (DCD)

Alt text

Use Cases

 Use CasTitel
UC01Sign In
UC02Sign Out
UC03Read a Discussion
 UC04Post a Contribution
 UC05Add a New Discussion
 UC06Add a new Topic
UC07Show Statistics

Systemoperationen

getNbrOfContributions

UC: UC07

Signatur: getNbrOfContributions()

Gibt die Anzahl Diskussionsbeitrage "Klasse Contribution" im gesamten Forum zurück.

  • Die Systemoperation zur Berechnung der Anzahl Beiträge "Contributions" wird so realisiert, dass über alle Stufen der Zugehörigkeithierarchie "containment hierarch" iteriert wird und die jeweiligen Zwischenresultate aufsummiert werden.
  • Alle oben aufgeführten Methoden müssen zusätzlich im DCD eingetragen werden. Beachten Sie die herarchische Nummerierung. Bezüglich Low Coupling .wird auf die Diskussion von getTotal() der Fallstudie verwiesen.
  • Das Addieren der Zwischenresultate findet in den jeweiligen "getNumberOfContributions()" Methoden statt und wird hier gar nicht modelliert.
  • Das Design direkt über User alle Contributions zu ermitteln, liefet eine falsche Anzahl, wennn Benutzer gelöscht werden!

### addNewDiscussion

UC: UC05

Signatur: addNewDiscussion(accessToken: byte[], userName: String, topicName: String, discussionName: String)

Es muss überprüft werden, ob so eine Diskussion bereits existiert und in diesem Fall muss eine Exception geworfen werden.

  • Vom Fassaden-Controller aus wird zuerst das Access-Token validiert. Dann wird das richti- ge Thema («Topic») gewählt. Auf dem Topic wird die Diskussion («Discussion») erzeugt. Die Diskussion muss dann noch mit dem Topic verknüpft werden.
  • Die Authentifizierung ist gemäss Aufgabenstellung ein Variationspunkt. Darum wird für die Validierung des Access-Tokens ein Interface «AccessTokenValidatorService» mit der Me- thode «isValidToken()» erstellt (analog für die Authentifzierung gemäss I in SOLID Interface Segregation). Zur Laufzeit kann dann eine konkrete Instanz eines AccessTokenValidator- Service dem Forum injiziert werden (Konstruktor).
  • Das Sequenzdiagramm zeigt im Wesentlichen nur den Erfolgsfall. Die Fehlerbehandlung ist in den Kommentaren angegeben. Ihre Modellierung mittels UML würde das Diagramm nicht nur aufblasen, sondern auch sehr unleserlich machen.
  • Alle oben aufgeführten Methoden müssen zusätzlich im DCD eingetragen werden (s. DCD auf der nächsten Seite). Die Zugriffe auf die Container Klassen wurden nicht explizit model- liert, dafür aber entsprechende Methoden auf der jeweiligen Domänenklasse angelegt (getXyzForName(...)). Diese können auch dafür verwendet werden, eine Exception zu wer- fen, wenn das Domänenobjekt mit dem gesuchten Namen nicht vorhanden ist. Werden die- se Tests explizit eingezeichnet, kann dies leicht zu einem unübersichtlichen Diagramm führen.
  • Die Implementation folgt exakt den Diagrammen. Für die Erzeugung der aktuellen «Instant» wurde auf die abstrakte Klasse «java.time.Clock» zurückgegriffen. Man kann eine Variante davon erzeugen, die wirklich die aktuelle Systemzeit mit der eingestellten Zeitzone reprä- sentiert. Es gibt aber auch eine Variante, die immer dieselbe Zeit liefert. Diese ist primär für Testfälle gedacht. Die Erzeugung der aktuellen «Instant» wird der Controller Klasse zuge- ordnet, da dadurch nur eine Instanz von Clock notwendig ist, die bei der Instanziierung dem Controller «Forum» übergeben wird.

Erkennen von unangemessener Sprache

Alt text

Alt text

Alt text

Lampentool

Flexible einsetzbare Lampensteuerung. Die dafür vorgesehenden Lampen sind bezüglich den Farbkombonennten Rot, Grün und Blau jeweils zwischen 0 und 100% einstellbar. Wie sie vielleicht wissen, ist jeder Farbton eine Mischung dieser 3 Grundfarben während Weiss alle 3 Komnponenten mit gleicher Stärke hat.

Um das Licht zu steuern, kommen Schalter zum Einsatz, die sowohl gedreht wie auch gedrückt werden können. Allerdings kennen die Schalter ihre aktuelle absolute Position nicht. Sie können nur der Lampensteuerung melden, ob sie gedrückt und in welche Richtung sie gedreht wurden.

Domänenmodell

Alt text

  • Die Lampensteuerung reagiert auf die Schalter-Ändeungen und verändert die Lampen, die dem Schalter zugeordnet sind.
  • Die genaue Art der veränderung hängt vom gewählten Betriebsmodus ab.
  • Ein Betriebsmodus ist fix einem Schalter zugeordnet.
  • Die verfügbaren Lampen und Schalter, die Zuordnung der Lampen zu den Schaltern und der Betriebsmodus für einen Schalter werden beim Start der Lampensteuerung definiert und sind dann konstant.
  • Es ist übrigens möglich, dass eine Lampe von mehr als einem Schalter angesteuert wird.

General Responsibility Assignment Software Patterns (GRASP)

Entwurfsmuster, mit denene die Zuständigkeit bestimmter Klassen objektorientierter Systeme festgelegt werden.

Information Expert

Ein allgemeines Prinzip des Objektentwurfs und der Zuweisung von Verantwortlichkeiten.

  • Eine Klasse sollte nur die Aufgaben ausführen, die ihr Name auch beschreibt.

Creator

Soll eine Instanz einer Klasse erstellt werden, wird ein Creator Pattern verwendet.

Anwenden wenn mindestens etwas zutrifft:

  • A eine Aggregation oder ein Kompositum von B ist
  • A registriert oder erfasst B-Objekte
  • A arbeitet eng mit B-Objekten zusammen oder hat eine enge Kopplung
  • A verfügt über Initialisierungsdaten für B

Controller

Ein Controller koordiniert / empfängt als erstes Objekte jenseits der UI-Schicht als Systemoperation.

  • Variante 1: Fassaden Controller (repräsentiert das System bzw. übergeordnetes System)
  • Variante 2: Use Case Controller (pro Use case eine künstliche Klasse)

Controller macht selber nur wenig und delegiert fast alles

Low Coupling

Kopplung = Mass für die Abhängigkeit von anderen Elementen.

  • Hohe Kopplung: Element ist von vielen anderen Elementen abhängig
  • Niedrige Kopplung: Element ist nur von wenigen anderen Elementen abhängig

Der Aufruf der Funktionen sollte in einer Linie geschehen.

High Cohesion

Mass für die Verwandtschat und Fokussierung eines Elements.

  • Hohe Kohäsion: Element erledigt nur wenige Aufgaben, die eng miteinander verwandt sind.
  • Geringe Kohäsion: Element das für viele unzusammenhängende Dinge verantwortlich ist.

Wie bei Low Coupling, sollten die Aufgaben gebündelt werden.

Polymorphism

Polymorphism ist das bündeln von typengleichen oder ähnlichen Verhalten.

Tier

Ein Hund ist auch ein Tier

Pure Fabrication

Künstliche Hilfsklassen erzeugen um dem Domänenmodell gleich zu sein. Wird nur verwendet, um eine bessere Wiederverwendbarkeit zu realisieren.

Indirection

Direkte Kopplung zwischen zwei oder mehreren Objekten vermeiden.

-> Vermittler

Protected Variants

Veränderungen im Objekt sollen keinen Einfluss auf andere Elemente haben.

-> Interfaces einführen

  • Spekulationen sind zu vermeiden, da dies zu unnötiger Komplexität führt.