Archiv der Kategorie: Allgemein

Allgemeine Dinge so rund um das Leben und Interessensgebiete die in keine Kategorie passen…

5 Dinge, die Du als YouTuber über das neue Urheberecht wissen solltest.

Kurzgefasst

Grundsätzlich wird YouTube stärker in die Pflicht genommen und ist dafür verantwortlich, was auf der Plattform angeboten wird. D.h. YouTube muss sich überlegen, wie es zukünftig dem neuen Gesetzesentwurf von Februar 2021 nachkommt.

Die kreative Bearbeitung von Fremdwerken ist erlaubt für:

  1. In größerem Umfang: Parodien, Zitate, Montagen
  2. Urheberechtlich geschützte Werke maximal bis zur Hälfte
    1. Der Rechteinhaber hat einen „Red Button“, mit dem er Uploads sofort sperren kann.
  3. 15 Sekunden eines Filmschnipsels, Musik und Ton (Sampling)
  4. Texte bis 160 Zeichen
  5. Beim Upload: Der Nutzer muss zukünftig YouTube signalisieren, dass es sich um einen erlaubten Inhalt handelt.

Meine persönlichen Fragen

Heisst das für die Rechteinhaber, sie müssen jmd. abstellen der allemöglichen hochgeladenen Inhalte prüft, ob damit persönliche Rechte verletzt wurden?

Ist es wirklich erlaubt einen Kinofilm bis zur Hälfte hochzuladen?

An euch!

Wenn ihr bereits mehr Dinge in Erfahrung gebracht habt, dann schreibt das bitte in die Kommentare!

7 Trinklieder und Gassenhauer aus 2020 mit Evergreen-Potential

Als Kind der 80er kennt man mit Sicherheit die alten Gassenhauer, die auf den Kirmessen vermischt mit Uff-da-da-Musik rauf und runtergedudelt wurden:

Alte Trinklieder der 70er / 80er … :

  • Eingekühlter Bommelunder
  • 10 kleine Jägermeister
  • Das Altbierlied
  • Saufen, saufen, saufen, saufen … (Schröders)
  • Was wollen wir trinken 7 Tage lang
  • Johny Walker
  • Heute trinken wir richtig
  • Skandal im Sperrbezirk
  • u.v.m. …

Wer glaubt es gäbe keine aktuellen guten Trinklieder die sich mit den alten Gassenhauern messen können, den muss ich eines besseren belehren :-).
Ich stelle nachfolgend ein paar sehr gut gelungene Trinklieder vor und wenn man sich reingehört hat wird man mir recht geben und auch der Meinung sein, dass der eine oder andere Song das Potential hat die alten Lieder zu übertreffen.

Bitte bedenkt dabei, das ihr die alten Hits mehrfach gehört habt und euer Gehör darauf trainiert ist, so gebt den nachfolgenden Liedern eine Chance:

Aktuelle Trinklieder der 2020er:

Da ist zunächst die Band „Feuerschwanz“, eine Mittelalter-Comedy-Band, welche mit ansehnlichen Choreographinnen, einer Geigerin und 4 weitern jungen Männern Mittelalterrock spielen. Hier gibt es zahlreiche Trinklieder und ich möchte an dieser Stelle „Metfest“ vorstellen, welches mir persönlich gut gefällt:

Feuerschwanz – Metfest

Als nächsten wären „Mr. Hurley & Die Pulveraffen“ zu erwähnen, welche sich dem Fantasy-Piraten-Kult verschworen haben. Ein Song mit sehr eingängigem Text wäre „Blau wie das mehr“:

Mr. Hurley und die Pulveraffen – Blau wie das Meer

Hier sind nochmal 2 Lieder von Knasterbart, wo der Sänger von „Mr. Hurley und die Pulveraffen“ auch mitsingt. Man darf allerdings nicht so auf die Zähne achte 😀 😀 😀 :

Knasterbart – Gossenabitur
Knasterbart – Kneipenschlägerei
Mr. Hurley und die Pulveraffen – Booty Island

Schandmaul … die Band die Nachts in der Endlosschleife auf KIKA läuft (also Vater kennt man das wenn die Kinder die „Bernd das Brot“ Folgen nachplappern weil sie nachts heimlich Fernsehen geschaut haben):

Schandmaul – Der Teufel hat den Schnaps gemacht

Und sozusagen als eines seiner Abschiedskonzerte wenige Monate vor dem Tod von Udo Jürgens gab es von Santiano eine Eigeninterpretation von „Der Teufel hat den Schnaps gemacht“ welche ich sehr gelungen fand:

Santiano – The devil made the rum for us

2020-08-02 Urlaubsbericht: Aqua Mundo Zugang im Center Park Het Merdaal zu Corona / COVID-19

Aufgrund der COVID-19 Situation ist der Zugang zum Aqua Mundo auf 2 Stunden beschränkt. Center Parcs informiert vor der Ankunft, dass man für das Buchen eines 2-Stunden Zeitfensters die Center-Parcs App benutzen kann, alternativ auch über die Rezeption, welche sich im Zentrum befindet, ein Zeitfenster buchen kann. Für die Nutzung der Center Parcs App benötigt man die E-Mailadresse, welche beim Tätigen der Buchung genutzt wurde. Außerdem wird darauf hingewiesen, dass jeder Besucher selbst dafür verantwortlich ist die Maßnahmen umzusetzen.

Die buchbaren Zeitfenster sind die folgenden:

  • Zeitfenster 1: 9.45h
  • Zeitfenster 2: 12.00h
  • Zeitfenster 3: 14.15h
  • Zeitfenster 4: 16.30h
  • Zeitfenster 5: 18.45h

Es wird darum gebeten keine Zeitfenster hintereinander zu buchen. Man soll vor jeder Aqua Mundo Nutzung frisch geduscht sein und die Badekleidung direkt unter den Klamotten tragen. Im Schwimmbad gibt es kleine Fächer in denen Platz für Geldbeutel / Handy und andere kleine Wertsachen ist. Für das Belegen des Faches ist eine 1-EURO-Münze erforderlich. Man muss seine Klamotten in eine mitgebrachte Tasche stecken und diese irgendwo im Aqua Mundo verstauen. Wir haben beim Einlass daher meistens die Badehose direkt angezogen und Badelatschen / Flip Flops getragen, die wir beim Einlass ausgezogen haben. Man wird im Prinzip über eine Route im Kreis durch das Aqua Mundo geschickt. Wenn man kleine Kinder dabei hat und etwas länger zum Umziehen braucht, hat man meistens keine Chance die Wildwasserbahn ohne längeres Anstehen / ohne längere Wartezeiten zu nutzen. Ist man im Schwimmbad mit den Badeschuhen durch rasches Entkleiden sofort schwimmfertig, lohnt sich der direkte Weg zur Wildwasserbahn, da dort keine Schlange existiert.

Abstände wurden zu unserer Zeit weniger eingehalten obwohl überall Schilder darauf hinweisen. Im Zentrum / Market Dome werden keine Masken getragen daher ist die erste Ankunft eher etwas gewöhnungsbedürftig da man das Gefühl hat man würde grobe fahrlässig handeln.

Wir haben natürlich die App genutzt um unsere Aqua Mundo Zeitfenster zu buchen, allerdings wird beim Eintritt in das Aqua Mundo nicht geprüft ob man wirklich eine Buchung zum aktuellen Zeitfenster hat. Wenn man die App nicht zum buchen benutzt, würde das keiner merken.

Es spielt im Grunde keine Rolle wie oft man ins Aqua Mundo geht. Wir haben für die Woche 3x 4 Karten für 4 Personen bekommen, sind allerdings 5x im Aqua Mundo gewesen, da wir nicht immer vollständig waren.

C# .NET : Create standard Unit Test Project and run a MsTest Unit Test Project with Standard Visual Studio Integration using Microsoft.VisualStudio.QualityTools.UnitTestFramework

Intention

Testing makes me happy 🙂 …

There are 4 common Unit Test Frameworks available as NuGet-Packages:

  • MsTest (Standard when creating a normal test project under VS2013)
  • MsTest v2
  • NUnit
  • xUnit

This short instruction shows how to set up the integrated standard Unit Test Framework without installing packages.

Approach

  • Right-click solution explorer and choose Add -> New Project
  • Under Visual C# choose Test -> Unit Test Project, change your name and click „ok“ on the lower right
  • Write Assert.isTrue(true); to have a passing test for test 🙂
  • In the Visual Studio 2013 main menu choose „Test -> Window -> Test explorer“ (this is the part i was searching for a while -> How to start the tests)
  • After adding the Test-Explorer choose „Run All“ to run the Unit Test.

Please Note: If you want to test another project with the test project, you have to Add a reference to it by right-clicking references -> Add…

Solution

Testing makes you happy 🙂

Aufbau und Betrieb eines Informationssicherheitsmanagementsystems (ISMS) für KRITIS-Betreiber

Problem

Alle KRITIS-Betreiber müssen seit 30.6.2019 laut IT-Sicherheitsgesetz alle 2 Jahren dem „Bundesamt für Sicherheit in der Informationstechnik“ die Unbedenklichkeit Ihrer IT Infrastruktur nachweisen. Im Zuge dessen können diese eine prüfende Stelle (interne Revision / externe Beratungsgesellschaft) beauftragen, welche die Maßnahmen des bspw. ISMS auditiert.

Ansatz

Um den Aufbau eines ISMS zu erleichtern bietet sich die folgende Vorgehensweise an:

QWare Risk Manager SQL Statements / Query

The following statements can be used to get the Risk Measure Tree from the Bayoonet QWare Risk Manager

First you should create the view QWareSegment:

SELECT        QwareRiskmanager.dbo.Project.ProjectNo, QwareRiskmanager.dbo.Project.ProjectTitle, QwareRiskmanager.dbo.Version.VersionId AS ProjectVersion, 
                         QwareRiskmanager.dbo.Version.ProductVersion, QwareRiskmanager.dbo.FunctionSet.Type, QwareRiskmanager.dbo.FunctionSetFunction.FunctionNo, 
                         QwareRiskmanager.dbo.[Function].Description AS FunctionDescription, QwareRiskmanager.dbo.FunctionHazard.HazardNo, 
                         QwareRiskmanager.dbo.Hazard.Description AS HazardDescription, QwareRiskmanager.dbo.FunctionHazardCause.CauseNo, 
                         QwareRiskmanager.dbo.Cause.Description AS CauseDescription, QwareRiskmanager.dbo.Measure.Description AS MeasureDescription, 
                         QwareRiskmanager.dbo.FunctionHazardCauseMeasure.MeasureNo, QwareRiskmanager.dbo.Measure.NewMeasureNo, 
                         QwareRiskmanager.dbo.FunctionSetFunction.FunctionId, QwareRiskmanager.dbo.FunctionHazard.HazardId, QwareRiskmanager.dbo.FunctionHazardCause.CauseId, 
                         QwareRiskmanager.dbo.FunctionHazardCauseMeasure.MeasureId, seva.Abbreviation AS AfterSeverityAbbreviation, 
                         sevb.Abbreviation AS BeforeSeverityAbbreviation, propa.Abbreviation AS AfterProbabilityAbbreviation, propb.Abbreviation AS BeforeProbabilityAbbreviation, 
                         QwareRiskmanager.dbo.FunctionHazardCause.CommentAfter, QwareRiskmanager.dbo.FunctionHazardCause.CommentBefore
FROM            QwareRiskmanager.dbo.Project LEFT OUTER JOIN
                         QwareRiskmanager.dbo.Version ON QwareRiskmanager.dbo.Project.ProjectId = QwareRiskmanager.dbo.Version.ProjectId LEFT OUTER JOIN
                         QwareRiskmanager.dbo.FunctionSet ON QwareRiskmanager.dbo.Version.VersionId = QwareRiskmanager.dbo.FunctionSet.VersionId LEFT OUTER JOIN
                         QwareRiskmanager.dbo.FunctionSetFunction ON 
                         QwareRiskmanager.dbo.FunctionSet.FunctionSetId = QwareRiskmanager.dbo.FunctionSetFunction.FunctionSetId LEFT OUTER JOIN
                         QwareRiskmanager.dbo.[Function] ON QwareRiskmanager.dbo.FunctionSetFunction.FunctionId = QwareRiskmanager.dbo.[Function].FunctionId LEFT OUTER JOIN
                         QwareRiskmanager.dbo.FunctionHazard ON 
                         QwareRiskmanager.dbo.FunctionSetFunction.FunctionSetFunctionId = QwareRiskmanager.dbo.FunctionHazard.FunctionSetFunctionId LEFT OUTER JOIN
                         QwareRiskmanager.dbo.FunctionHazardCause ON 
                         QwareRiskmanager.dbo.FunctionHazard.FunctionHazardId = QwareRiskmanager.dbo.FunctionHazardCause.FunctionHazardId LEFT OUTER JOIN
                         QwareRiskmanager.dbo.FunctionHazardCauseMeasure ON 
                         QwareRiskmanager.dbo.FunctionHazardCause.FunctionHazardCauseId = QwareRiskmanager.dbo.FunctionHazardCauseMeasure.FunctionHazardCauseId LEFT OUTER
                          JOIN
                         QwareRiskmanager.dbo.Measure ON 
                         QwareRiskmanager.dbo.FunctionHazardCauseMeasure.MeasureId = QwareRiskmanager.dbo.Measure.MeasureId LEFT OUTER JOIN
                         QwareRiskmanager.dbo.Hazard ON QwareRiskmanager.dbo.FunctionHazard.HazardId = QwareRiskmanager.dbo.Hazard.HazardId LEFT OUTER JOIN
                         QwareRiskmanager.dbo.Cause ON QwareRiskmanager.dbo.FunctionHazardCause.CauseId = QwareRiskmanager.dbo.Cause.CauseId LEFT OUTER JOIN
                         QwareRiskmanager.dbo.Severity AS seva ON QwareRiskmanager.dbo.FunctionHazardCause.SeverityAfterId = seva.SeverityId AND 
                         QwareRiskmanager.dbo.Version.VersionId = seva.VersionId LEFT OUTER JOIN
                         QwareRiskmanager.dbo.Severity AS sevb ON QwareRiskmanager.dbo.FunctionHazardCause.SeverityBeforeId = sevb.SeverityId AND 
                         QwareRiskmanager.dbo.Version.VersionId = sevb.VersionId LEFT OUTER JOIN
                         QwareRiskmanager.dbo.Probability AS propa ON QwareRiskmanager.dbo.FunctionHazardCause.ProbabilityAfterId = propa.ProbabilityId AND 
                         QwareRiskmanager.dbo.Version.VersionId = propa.VersionId LEFT OUTER JOIN
                        QwareRiskmanager.dbo.Probability AS propb ON QwareRiskmanager.dbo.FunctionHazardCause.ProbabilityBeforeId = propb.ProbabilityId AND 
                         QwareRiskmanager.dbo.Version.VersionId = propb.VersionId

After that you should create the view QWareImport that gives us the Risk Measure Tree:

CREATE VIEW [dbo].[QWareImport]
AS
SELECT     CAST(REPLACE(RTRIM(REPLACE((ISNULL(FunctionNo, '') + '+' + ISNULL(HazardNo, '') + '+' + ISNULL(CauseNo, '') + '+' + ISNULL(MeasureNo, '')), '+', ' ')), ' ', '+') 
                      AS varchar(255)) AS BB_ItemID, CAST(ProjectNo AS varchar(255)) AS ProjectNo, CAST(ProjectVersion AS varchar(255)) AS ProjectVersion, 
                      CAST(ProductVersion AS varchar(255)) AS ProductVersion, ISNULL(CAST(FunctionId AS varchar(255)), '') AS FunctionId, ISNULL(CAST(FunctionNo AS varchar(255)), '') 
                      AS BB_FID, ISNULL(CAST(FunctionDescription AS varchar(2048)), '') AS BB_FText, ISNULL(CAST(HazardId AS varchar(255)), '') AS HazardId, 
                      ISNULL(CAST(HazardNo AS varchar(255)), '') AS BB_HID, ISNULL(CAST(HazardDescription AS varchar(2048)), '') AS BB_HText, ISNULL(CAST(CauseId AS varchar(255)), 
                      '') AS CauseId, ISNULL(CAST(CauseNo AS varchar(255)), '') AS BB_CID, ISNULL(CAST(CauseDescription AS varchar(2048)), '') AS BB_CText, 
                      ISNULL(CAST(MeasureId AS varchar(255)), '') AS MeasureId, ISNULL(CAST(MeasureNo AS varchar(255)), '') AS BB_MID, 
                      ISNULL(CAST(MeasureDescription AS varchar(2048)), '') AS BB_MText, ISNULL(CAST(AfterSeverityAbbreviation AS varchar(255)), '') AS BB_EAM_Severity, 
                      ISNULL(CAST(BeforeSeverityAbbreviation AS varchar(255)), '') AS BB_EBM_Severity, ISNULL(CAST(AfterProbabilityAbbreviation AS varchar(255)), '') 
                      AS BB_EAM_Probability, ISNULL(CAST(BeforeProbabilityAbbreviation AS varchar(255)), '') AS BB_EBM_Probability, ISNULL(CAST(CommentAfter AS varchar(2048)), '') 
                      AS BB_CCommentAM, ISNULL(CAST(CommentBefore AS varchar(2048)), '') AS BB_CCommentBM, ISNULL(CAST(NewMeasureNo AS varchar(2048)), '') 
                      AS BB_MNr
FROM         DT_Qware.dbo.QWareSegment
UNION
SELECT     CAST(REPLACE(RTRIM(REPLACE((ISNULL(FunctionNo, '') + '+' + ISNULL(HazardNo, '') + '+' + ISNULL(CauseNo, '')), '+', ' ')), ' ', '+') AS varchar(255)) AS BB_ItemID, 
                      CAST(ProjectNo AS varchar(255)) AS ProjectNo, CAST(ProjectVersion AS varchar(255)) AS ProjectVersion, CAST(ProductVersion AS varchar(255)) AS ProductVersion, 
                      ISNULL(CAST(FunctionId AS varchar(255)), '') AS FunctionId, ISNULL(CAST(FunctionNo AS varchar(255)), '') AS BB_FID, 
                      ISNULL(CAST(FunctionDescription AS varchar(2048)), '') AS BB_FText, ISNULL(CAST(HazardId AS varchar(255)), '') AS HazardId, 
                      ISNULL(CAST(HazardNo AS varchar(255)), '') AS BB_HID, ISNULL(CAST(HazardDescription AS varchar(2048)), '') AS BB_HText, ISNULL(CAST(CauseId AS varchar(255)), 
                      '') AS CauseId, ISNULL(CAST(CauseNo AS varchar(255)), '') AS BB_CID, ISNULL(CAST(CauseDescription AS varchar(2048)), '') AS BB_CText, '' AS MeasureId, 
                      '' AS BB_MID, '' AS BB_MText, '' AS BB_EAM_Severity, '' AS BB_EBM_Severity, '' AS BB_EAM_Probability, '' AS BB_EBM_Probability, '' AS BB_CCommentAM, 
                      '' AS BB_CCommentBM, '' AS BB_MNr
FROM         DT_Qware.dbo.QWareSegment
UNION
SELECT     CAST(REPLACE(RTRIM(REPLACE((ISNULL(FunctionNo, '') + '+' + ISNULL(HazardNo, '')), '+', ' ')), ' ', '+') AS varchar(255)) AS BB_ItemID, CAST(ProjectNo AS varchar(255))
                       AS ProjectNo, CAST(ProjectVersion AS varchar(255)) AS ProjectVersion, CAST(ProductVersion AS varchar(255)) AS ProductVersion, 
                      ISNULL(CAST(FunctionId AS varchar(255)), '') AS FunctionId, ISNULL(CAST(FunctionNo AS varchar(255)), '') AS BB_FID, 
                      ISNULL(CAST(FunctionDescription AS varchar(2048)), '') AS BB_FText, ISNULL(CAST(HazardId AS varchar(255)), '') AS HazardId, 
                      ISNULL(CAST(HazardNo AS varchar(255)), '') AS BB_HID, ISNULL(CAST(HazardDescription AS varchar(2048)), '') AS BB_HText, '' AS CauseId, '' AS BB_CID, 
                      '' AS BB_CText, '' AS MeasureId, '' AS BB_MID, '' AS BB_MText, '' AS BB_EAM_Severity, '' AS BB_EBM_Severity, '' AS BB_EAM_Probability, 
                      '' AS BB_EBM_Probability, '' AS BB_CCommentAM, '' AS BB_CCommentBM, '' AS BB_MNr
FROM         DT_Qware.dbo.QWareSegment
UNION
SELECT     CAST(REPLACE(RTRIM(REPLACE((ISNULL(FunctionNo, '')), '+', ' ')), ' ', '+') AS varchar(255)) AS BB_ItemID, CAST(ProjectNo AS varchar(255)) AS ProjectNo, 
                      CAST(ProjectVersion AS varchar(255)) AS ProjectVersion, CAST(ProductVersion AS varchar(255)) AS ProductVersion, ISNULL(CAST(FunctionId AS varchar(255)), '') 
                      AS FunctionId, ISNULL(CAST(FunctionNo AS varchar(255)), '') AS BB_FID, ISNULL(CAST(FunctionDescription AS varchar(2048)), '') AS BB_FText, '' AS HazardId, 
                      '' AS BB_HID, '' AS BB_HText, '' AS CauseId, '' AS BB_CID, '' AS BB_CText, '' AS MeasureId, '' AS BB_MID, '' AS BB_MText, '' AS BB_EAM_Severity, 
                      '' AS BB_EBM_Severity, '' AS BB_EAM_Probability, '' AS BB_EBM_Probability, '' AS BB_CCommentAM, '' AS BB_CCommentBM, '' AS BB_MNr
FROM         DT_Qware.dbo.QWareSegment

To query a unique Risk Project in the appropriate version you have to perform the following selection

SELECT BB_ItemID, 
		ProjectVersion, 
		ProductVersion, 
		FunctionId, 
		BB_FID, 
		BB_FText, 
		HazardId, 
		BB_HID, 
		BB_HText, 
		CauseId, 
		BB_CID, 
		BB_CText, 
		MeasureId, 
		BB_MID, 
		BB_MText, 
		BB_EAM_Severity, 
		BB_EBM_Severity, 
		BB_EAM_Probability, 
		BB_EBM_Probability, 
		BB_CCommentAM, 
		BB_CCommentBM,
		BB_MNr
FROM DP_Qware.dbo.QWareImport
WHERE ProjectNo='**ProjectNo**' AND ProductVersion='**ProductVersion**' 
ORDER BY 1

IBM Doors DXL: Layout DXL where the traceability wizard fails

Problem

In some cases you will run into trouble when you want to use the standard function of Doors to create a traceability for a depth of 2 that is not going into the same direction. To solve this problem the implementation of an own Layout DXL is necessary.

The example below shows the situation with depth 2 and the same Linkmodule in which you can use the standard function to create a Layout DXL column:

Linkmodule 1
Linkmodule 1
A
A
Linkmodule 1
Linkmodule 1
B
B
C
C

The next situation, which is not possible to master with the standard doors function, shows the problem where depth two with two different direction and link module types gives the needed traceability information for the report.

Linkmodule 1
Linkmodule 1
A
A
B
B
C
C
Linkmodule 2
Linkmodule 2
Object in source module
Object in source module
Outgoing link of type „Linkmodule 1“ (i.e. „Creates“)
[Not supported by viewer]
Incominglink of type „Linkmodule 2 (i.e. „Triggers“)
[Not supported by viewer]
not possible withoud Standard Traceability Wizard
not possible withoud Standard Traceability Wizard

Approach – Ansatz

To get the needed traceability information within a module, you can add a layout dxl column by clicking on the column header of the current selected view in the module and add a layout DXL column. This layout DXL column can iterate through the outgoing modules of the current object, catch all objects that are linked and look for incoming links on it.

Solution – Lösung

//
/*******************************************************************************
* Author: Björn Karpenstein 
* Stakeholder: Björn Karpenstein
*
* Subversion
* ===================================================
* $Rev::               $:  Revision of last commit
* $Author::            $:  Author of last commit
* $Date::              $:  Date of last commit
*
* Change Control
* ==============
* n/a
*
* Description
* ===========
* This script gives the traceability over 2 modules with two different diection
* and module types
*******************************************************************************/
string firstLinkModule  = "/Product Lifecycle Management/10 Administration/Create";
string secondLinkModule = "/Product Lifecycle Management/10 Administration/Triggers";

Skip dublettenRaus=createString;

pragma encoding,"UTF-8";
pragma runLim, 0;

//Object obj=current;
 
Link lnk;
Link inLnk;
LinkRef lnkRef;
ModName_ otherMod = null;
 
for lnk in obj->firstLinkModule do
{
	string tmn=fullName target(lnk);
	
	if(!open module tmn)
	{
		read(tmn,false);
	}
	
	Object keypoint = target(lnk);
	//display identifier(keypoint) "\n";
	
	if(null keypoint)
	{
		 //display "no link to keypoint";
	}
	else
	{	     
		//display "keypoint : " identifier(keypoint) "";
		for lnkRef in keypoint<-secondLinkModule do
		{
		  //display "drin";
		  otherMod = module (sourceVersion lnkRef);
		  if (!null otherMod) 
		  {
		    if ((!isDeleted otherMod) && (null data(sourceVersion lnkRef))) 
		    {
		      load((sourceVersion lnkRef),false);
		    }
		  }
		}
		     
		for inLnk in keypoint<-secondLinkModule do
		{
		  // Get In-Link Object
		  Object phase = source inLnk;
		  if ( isDeleted(phase) || null(phase) ) continue;
		     
		  string dummy="";
		  if(!find(dublettenRaus, phase."Object Text" "", dummy))
		  {
			  put(dublettenRaus, phase."Object Text" "", phase."Object Text" "");
		  }
		}
	}
}

int phaseCounter=0;

// Iteration through the SkipList
for myIterator in dublettenRaus do
{
	string keyValue = (string key(dublettenRaus));
	string currentObject = null;
	
	if(find(dublettenRaus, keyValue, currentObject))
	{
	   display keyValue;
	}
}

delete dublettenRaus;

Sparx Systems Enterprise Architect + SQL : Interpretation der Linkdarstellung / Link Layout eines Diagrams über das relationale EA Datenmodell

Problem

Über das relationale Datenmodell des Enterprise Architects soll die Geometrie von Links auf Diagrammen rekonstruiert werden. Die nötigen Felder sind in den Tiefen der Datenstrukturen aus Tabellen und Attributen in einem Memo-/Freitext-Feld versteckt.

Ich entwickle zur Zeit eine eigene Interpretation des EA-Renderers mit GoJS, wo ich auf das Problem gestoßen bin, dass ich aufgrund der unterschiedlichen Connectorstyles, welche für einen Link auf unterschiedlichen Diagrammen eine andere Geometrie vorweisen kann, den Endpunkt nicht ermitteln kann. Eine Lösung hierzu möchte ich hier ausarbeiten.

Analyse

Relationen

Ein Link/Connector (Tabelle „t_connector„) kann auf mehreren Diagrammen unterschiedliche Darstellungen haben. Die Geometrie des Links auf einem Diagramm wird über das Attribut „Geometry“ der Tabelle „t_diagramlinks“ bestimmt. Das Attribut „Geometry“ ist ein Langtext-/Memo-Feld und wird im folgenden einer Analyse unterzogen, da sich im Internet nur sehr spärlich Informationen über den Aufbau und die Bedeutung des Doppelpunkt-separierten Strings finden.

Die Tabelle „t_diagramlinks“

“ A DiagramLink is an object that holds display information on a connector between two elements in a specific diagram. It includes, for example, the custom points and display appearance. It can be accessed from the Diagram DiagramLinks collection. “

https://sparxsystems.com/enterprise_architect_user_guide/
14.0/automation/diagramlinks.html
AttributeRemarks

ConnectorID
Long
Notes: Read/Write
The ID of the associated connector.

DiagramID
Long
Notes: Read/Write
The local ID for the associated diagram.

Geometry
String (Memo-Field)
Notes: Read/Write
The geometry associated with the current connector in this diagram.

HiddenLabels
Boolean
Notes: Indicates if this connector’s labels are hidden on the diagram.

InstanceID
Long
Notes: Read only
The connector identifier for the current model.

IsHidden
Boolean
Notes: Read/Write
Indicates if this item is hidden or not.

LineColor
Long
Notes: Sets the line color of the connector.
Set to -1 to reset to the default color in the model.

LineStyle
Long
Notes: Sets the line style of the connector.
1 = Direct
2 = Auto Routing
3 = Custom Line
4 = Tree Vertical
5 = Tree Horizontal
6 = Lateral Vertical
7 = Lateral Horizontal
8 = Orthogonal Square
9 = Orthogonal Rounded

LineWidth

Long
Notes: Sets the line width of the connector.

ObjectType
ObjectType
Notes: Read only
Distinguishes objects referenced through a Dispatch interface.

Path
String
Notes: Read/Write
The path of the connector in this diagram.

SourceInstanceUID
String
Notes: Read only
Returns the Unique Identifier of the source object.

SuppressSegment
Boolean
Notes: Indicates whether the connector segments are suppressed.

Style
String
Notes: Read/Write
Additional style information; for example, color or thickness.

TargetInstanceUID
String
Notes: Read only
Returns the Unique Identifier of the target object.
Der String „Geometry“ wird in MDB Viewer Plus als (MEMO) dargestellt, das MemoFeld kann allerdings rechts eingeblendet werden. Dort tauchen einige weitere versteckte Attribute für die Geometry der Linie auf, welche im nachfolgendem Analysiert werden
Die Auswahl des LineStyles für einen Connector, wie oben im Screenshot dargestellt, bestimmt die Darstellung.
Die LineStyles werden in der Tabelle [t_diagramlinks] und über Mode={1,2,3} und dem optionalen Zusatz TREE={V,H,OS,OR,LV,LH}:Mode=3 für Mode „3“ gespeichert.
Das Hauptproblem: Das Datenmodell besitzt nicht die Information, an welcher Seite der End-Knoten/das Ziel-Rechteck geschnitten wird. Lediglich über das Attribut t_diagrammlinks.Geometry lässt sich über den CSV-Value „Edge“ die Startseite des Startknotens bestimmen.

Fremdmeinungen einholen – ein Ausflug in die Forenwelt

Ich habe mich aus Gründen der Kollaboration entschieden, die Analysen in Stackoverflow und EA fortzuführen. Hier finden sich die Links zu meinem Beitrag:

https://stackoverflow.com/questions/55341368/enterprise-architect-how-to-get-the-edge-of-the-end-node-using-a-sql-query-on

https://www.sparxsystems.com/forums/smf/index.php/topic,42536.0.html

https://matheplanet.de/matheplanet/nuke/html/viewtopic.php?topic=241211

Fazit: Obwohl mir kein Forum eine nutzbare Lösung angeboten hat, lohnt es sich sich neue Anregungen zu dem Problem einzuholen

Lösung für das Problem mit der fehlenden Ecke des Zielknoten (Target Edge) – Rechteckschnitt

Im Datenmodell wird die Mouse-Release Position über Geometry EX/EY (Geometry) gespeichert, was allerdings nicht der Schnittpunkt mit dem Rechteck ist.

Mit 4 Geraden lässt sich das Rechteck bestimmen (obgleich natürlich auch Punkte existieren, die nicht auf dem Rechteck liegen).

Seite 1 der Lösung
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Erzeuge eine eigene Link-Klasse für das Routing der Pfeile, die von Hand gezogen wurden 
// und über spezielle Attribute in der EAP-Datei definiert werden
// Ruft die Superklasse von go.Link im Konstruktor auf
function MultiNodePathLink() {
    go.Link.call(this);
}
go.Diagram.inherit(MultiNodePathLink, go.Link); // Erben von go.Link

// ignores this.routing, this.adjusting, this.corner, this.smoothness, this.curviness
/** @override */
MultiNodePathLink.prototype.computePoints = function () {

    // Die this Referenz ist hier ist ein geerbter ein go.Link. der bei Links
    var startNode = this.fromNode;
    var startNodeX = startNode.location.M;  // X-Koordinate vom Startknoten
    var startNodeY = startNode.location.N; // Y-Koordinate vom Startknoten

    var endNode = this.toNode;
    var endNodeX = endNode.location.M;  // X-Koordinate vom Startknoten
    var endNodeY = endNode.location.N; // Y-Koordinate vom Startknoten
   
    var startNodeData = startNode.data; // Das sind die Daten
    var endNodeData = endNode.data; // Das sind die Daten

    // Die Link-Daten
    var linkProperties = this.data;
    //** Das Feld Style in [t_diagramlink] bestimmt die Connector-Darstellung  **/
    // https://www.capri-soft.de/blog/?p=2904
    /*
     *  1 = Direct                    Mode=1
     *  2 = Auto Routing              Mode=2
     *  3 = Custom Line               Mode=3
     *  4 = Tree Vertical             Mode=3;TREE=V
     *  5 = Tree Horizontal           Mode=3;TREE=H
     *  6 = Lateral Vertical          Mode=3;TREE=LV
     *  7 = Lateral Horizontal        Mode=3;TREE=LH
     *  8 = Orthogonal Square         Mode=3;TREE=OS
     *  9 = Orthogonal Rounded        Mode=3;TREE=OR
     */
    var styleStringArray = linkProperties.style.split(";");
    var mode = -1;
    var tree = '';
    for (var i = 0; i < styleStringArray.length; i++) {
        if (styleStringArray[i].trim().indexOf('Mode=') > -1) {
            mode = styleStringArray[i].replace('Mode=', '');
        }

        if (styleStringArray[i].trim().indexOf('TREE=') > -1) {
            tree = styleStringArray[i].replace('TREE=', '');
        }
    }



    // In der Tabelle t_diagramlinks in der Freitextspalte "Geometry" wird in einem CSV-String 
    // gespeichert, wie der Link letztendlich auf dem Diagram gezogen wurde
    var geometryString = linkProperties.geometry.split(";");
    var sx, sy, ex, ey, edge;
    for (var i = 0; i < geometryString.length; i++) {
        // SX and SY are relative to the centre of the start object 
        if (geometryString[i].trim().indexOf('SX=') > -1) sx = geometryString[i].replace('SX=', '');
        if (geometryString[i].trim().indexOf('SY=') > -1) sy = geometryString[i].replace('SY=', '');

        // EX and EY are relative to the centre of the end object
        if (geometryString[i].trim().indexOf('EX=') > -1) ex = geometryString[i].replace('EX=', '');
        if (geometryString[i].trim().indexOf('EY=') > -1) ey = geometryString[i].replace('EY=', '');

        // EDGE ranges in value from 1-4, with 1=Top, 2=Right, 3=Bottom, 4=Left (Outgoing Point of the Start Object)
        if (geometryString[i].trim().indexOf('EDGE=') > -1) edge = geometryString[i].replace('EDGE=', '');
    }
    


    // Hier beginnt das Custom-Routing
    if (typeof (sx) === "undefined" || typeof (sy) === "undefined" || typeof (ex) === "undefined" || typeof (ey) === "undefined" || typeof (edge) === "undefined") {
        return;
    }
       
    this.clearPoints();

    if (linkProperties.start_object_name == 'System Verification Test Reports' && linkProperties.end_object_name == 'System test specifications') {
        var test = 'irrsinn';
    }
    // Hier werden die Wege definiert für das gecustomizte Link Routing
    // Geht der Link nach oben oder unten wird die Y-Koordinate des Startknotens genutzt (Weil Orthogonales Routing)
    var startConnX = null;
    var startConnY = null;
    if (edge == 1) { // Ecke oben
        startConnX = Math.abs(startNodeX) + Math.abs((startNode.actualBounds.width / 2) + new Number(sx));
        startConnY = Math.abs(startNodeY);
    }
    else if (edge == 3) { // Ecke unten
        startConnX = Math.abs(startNodeX) + Math.abs((startNode.actualBounds.width / 2) + new Number(sx));
        startConnY = Math.abs(startNodeY) + new Number(startNode.actualBounds.height);
    }
    else if (edge == 2) { // Ecke rechts
        startConnX = Math.abs(startNodeX) + startNode.actualBounds.width;
        startConnY = Math.abs(startNodeY) + Math.abs((startNode.actualBounds.height / 2) - new Number(sy));
    }
    else if (edge == 4) { // Ecke links
        startConnX = new Number(Math.abs(startNodeX));
        startConnY = Math.round(startNodeY) + Math.round((startNode.actualBounds.height / 2) - new Number(sy));
    }
    else {
        alert('Die Edge konnte nicht entdeckt werden! Ist der Geometry String in der EAP Datei richtig?');
    }

    this.addPoint(new go.Point(Math.round(startConnX), Math.round(startConnY)));

    // Abfrage: Gibt es einen letzten Path Punkt?
    var lastPathPunkt=false;
    var lastPathPunktX, lastPathPunktY;

    if (mode != 1)
    {
        // Routing über die Zwischenwege
        if (typeof linkProperties.conn_path !== "undefined" && linkProperties.conn_path !== "") {
            var splittedArray = linkProperties.conn_path.split(";");
            if (splittedArray.length > 1) {
                // Hier ist mindestens ein Wert vorhanden da auch der erste mit Semikolon abgeschlossen wird im Path vom EA
                for (var i = 0; i < splittedArray.length - 1; i++) {
                    var einMittelPunkt = splittedArray[i];
                    var mittelPunktArray = einMittelPunkt.split(":");
                    this.addPoint(new go.Point(Math.abs(new Number(mittelPunktArray[0])), Math.abs(new Number(mittelPunktArray[1]))))
                    lastPathPunktX = Math.abs(new Number(mittelPunktArray[0]));
                    lastPathPunktY = Math.abs(new Number(mittelPunktArray[1]));
                    lastPathPunkt = true;
                }
            }
        }
    }

    // Wenn es keinen Pfad gab,muss der letzte Punkt mit dem Startknoten identisch sein
    if (lastPathPunkt == false) {
        lastPathPunktX = Math.abs(Math.round(startConnX));
        lastPathPunktY = Math.abs(Math.round(startConnY));
    }

    // End-Routing
    // Der Endpunkt in EA in Document Coordinates
    var endConnX = Math.abs(endNodeX) + Math.abs((endNode.actualBounds.width / 2) + new Number(ex));
    var endConnY = Math.abs(endNodeY) + Math.abs((endNode.actualBounds.height / 2) - new Number(ey));

    // Spezialfälle bei horizontalen und vertikalen Linien:
    if (endConnX == lastPathPunktX) {
        // Es liegt eine vertikale Gerade (z.B. von oben nach unten) vor
        this.addPoint(new go.Point(Math.round(lastPathPunktX), Math.round(lastPathPunktY)));
        this.addPoint(new go.Point(Math.round(endConnX), Math.round(endConnY)));

    } else if (endConnY == lastPathPunktY) {
        // Es liegt eine horizontale Gerade (z.B. von rechts nach links) vor
        this.addPoint(new go.Point(Math.round(lastPathPunktX), Math.round(lastPathPunktY)));
        this.addPoint(new go.Point(Math.round(endConnX), Math.round(endConnY)));
    } else {
        // Es ist keine Gerade sondern ein Gerade, die mit y=m*x+b beschrieben werden kann

        // 1.) Gerade zwischen Start- und Endpunkt ermittelnhn
        //      Ye-Ys
        //  m = -----    b=Ys-m*Xs oder b=Ye-m*Xe
        //      Xe-Xs
        var m = (endConnY - lastPathPunktY) / (endConnX - lastPathPunktX);
        var b = lastPathPunktY - m * lastPathPunktX

        // 2.) Ermittlung der horizontalen und vertikalen Geraden des Rechteckes und dem Schnittpunkten
        // Die Geraden, die das Rechteck definieren:
        var rY1 = endNodeY;
        var rY2 = endNodeY + endNode.actualBounds.height;
        var rX1 = endNodeX;
        var rX2 = endNodeX + endNode.actualBounds.width;
        // (rX1, rY1) -zu-> (rX2, rY2)
        
        // Horizontale Geraden: 
        //     y - b
        // x = -----
        //       m


        var lengthToPoint = [];

        var sX1 = (rY1 - b) / m; // S1(sX1|rY1)
        if (sX1 >= rX1 && sX1 <= rX2) {
            // Der Schnittpunkt sX1 ist am Rechteck
            // Distanz: d=SQRT((y2-y1)^2+(x2-x1)^2)
            var dS1 = Math.sqrt(Math.pow(rY1 - lastPathPunktY, 2) + Math.pow(sX1 - lastPathPunktX, 2));

            lengthToPoint.push({
                "distanz": dS1,
                "x": sX1,
                "y": rY1
            });

        }

        var sX2 = (rY2 - b) / m; // S2(sX2|rY2)
        if (sX2 >= rX1 && sX2 <= rX2) {
            // Der Schnittpunkt sX2 ist am Rechteck
            // Distanz: d=SQRT((y2-y1)^2+(x2-x1)^2)
            var dS2 = Math.sqrt(Math.pow(rY2 - lastPathPunktY, 2) + Math.pow(sX2 - lastPathPunktX, 2));

            lengthToPoint.push({
                "distanz": dS2,
                "x": sX2,
                "y": rY2
            });
        }

        // Vertikale Geraden:
        //
        // y = m*x + b

        var sY1 = m * rX1 + b; // S3(rX1|sY1)
        if (sY1 >= rY1 && sY1 <= rY2) {
            // Der Schnittpunkt sY1 ist am Rechteck
            // Distanz: d=SQRT((y2-y1)^2+(x2-x1)^2)
            var dS3 = Math.sqrt(Math.pow(sY1 - lastPathPunktY, 2) + Math.pow(rX1 - lastPathPunktX, 2));

            lengthToPoint.push({
                "distanz": dS3,
                "x": rX1,
                "y": sY1
            });
        }

        var sY2 = m * rX2 + b; // S4(rX2|sY2)
        if (sY2 >= rY1 && sY2 <= rY2) {
            // Der Schnittpunkt sY2 ist am Rechteck
            // Distanz: d=SQRT((y2-y1)^2+(x2-x1)^2)
            var dS4 = Math.sqrt(Math.pow(sY2 - lastPathPunktY, 2) + Math.pow(rX2 - lastPathPunktX, 2));

            lengthToPoint.push({
                "distanz": dS4,
                "x": rX2,
                "y": sY2
            });
        }

        // Sortiere alle Punkte nach Distanz - der mit der kleinsten Entfernung isses
        lengthToPoint.sort(function (a, b) { return a.distanz - b.distanz });

        if (lengthToPoint.length > 0)
        {
            this.addPoint(new go.Point(Math.round(lengthToPoint[0].x), Math.round(lengthToPoint[0].y)));
        }
        else
        {
            this.addPoint(new go.Point(Math.round(lastPathPunktX), Math.round(lastPathPunktY)));
        }

        
    }

    return true;
};
// end MultiNodePathLink class