NNSpace – The next one

Share

Jetzt ist es wieder mal so weit. Wie jedes Jahr um diese Zeit, endlich … nein es ist noch nicht Weihnachten! Ich habe mich dazu durchgerungen mit einer neuen Version von NNSpace anzufangen. Mein Plan ist es im Moment alle Algorithmen, Ausführungspläne und so weiter als Activities/Workflows mit der WF 4 auszulagern. Das sollte möglichst grafisch passieren,  nur die Grundbausteine sollen noch in C# implementiert werden. Für einen Algorithmus wie z.B. die Backpropagation hat das mehrere Vorteile:

  • Man versteht den Algorithmus! Selbst als Programmierer blickt man bei den Algorithmen im Code kaum durch. Ich habe bei der letzten Version (CTP 2) regelmäßig Stunden damit zugebracht irgendwelche Fehler in den Algorithmen zu suchen .. Mit einer grafischen Übersicht dürfte das etwas einfacher werden. Außerdem kann man so besser erklären was passiert.
  • Der Algorithmus ist sehr dynamisch! Wenn wir die Workflows als XAML abspeichern und laden, kann man das jederzeit ändern. Das kann dann sogar der Benutzer machen, wenn er ein bisschen Durchblick hat.

Also, wie muss man sich das dann vorstellen? Ungefähr so:

Das ist mein erster Entwurf für einen Backpropagation-Workflow. Ihr erinnert euch sicherlich noch an die Backprop, also:

  1. Wir nehmen ein feed-forward Netz (Verbindungen nur in eine Richtung) und führen das aus (Forward Execute)
  2. Wir vergleichen das Ergebnis mit einem vorgegebenen Ziel-Vektor und berechnen den quadratischen Fehler (MSE)
  3. Wir berechnen die Ableitungen des Fehlers nach den Gewichten (die Delta-Werte sind hier zwischengespeicherte Teil-Ableitungen, die wir vorher cachen um Performance zu sparen)
  4. Wir aktualisieren die Gewichte anhand der Ableitungen
  5. Wir fangen wieder von vorne an, bis wir der Meinung sind, dass es gut ist

Dafür brauchen wir soweit erstmal nur folgende Code-Activities:

  • Mean Squared Error (quadratischer Fehler) mit Ableitung
  • Sigmoid für die Aktivierungsfunktionen mit Ableitung
  • For-Activity zum durchlaufen der Units, also z.B. zum Berechnen vom NetInput usw. (scheinbar gibt es gar keine For im .NET Framework, also hab ich fix mal selber eine gebastelt)

Innen drin sieht das dann z.B. so aus (für die Ausführung):

und innerhalb von “Calculate Hidden States” werden zunächst die net-Inputs zusammengezält und dann die Ergebnisse gesammelt durch die sigmoide Aktivierungsfunktion gezogen:

Eigentlich cool oder? Durch die Möglichkeit die Dinger im Designer zusammenzuklappen bleibt das Ganze auch immer schön übersichtlich. Ich bin selber gespannt, wie es jetzt noch weitergeht. Auf jeden Fall könnte es eventuell mit der Performance Probleme geben, wenn ja, müsste ich nochmal umdenken. Es stehen also nach der Logik noch ein paar Tests und dann die Integration in den Rehosted Workflow Designer an.

Visual Studio 2012 Metro Styles für WPF–Teil 1

Share

Obwohl viele bei dem Wort “Metro” wahrscheinlich an Windows 8 und an das “Kachelwand”-Startmenü denken werden, folgt auch der Visual Studio 2012 Style dem Metro-Prinzip.

Weil sich dieser VS-Metro-Style für komplizierte Anwendungen, die nicht unbedingt dafür gedacht sind auf irgendwelchen Smartphones oder so zu laufen, viel besser eignet, habe ich ein paar Styles für Standard Controls und eine Beispielanwendung dazu gebastelt.

Screenshots

Screen1Screen2

Übersicht

Die Styles sind in mehrere ResourceDictionaries aufgeteilt. Für jedes Control gibt es ein eigenes Dictionary. Die Styles sind jeweils mit einem Schlüssel versehen, sodass man sie explizit referenzieren muss. Das “Styles.xaml” ResourceDictionary fasst alle Styles zusammen und setzt sie als Standard (ohne Key).

Komplettes Styleset verwenden

App.xaml:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/Selen.Wpf.SystemStyles;component/Styles.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

 

Nur Teile davon

App.xaml:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary 
                Source="pack://application:,,,/Selen.Wpf.SystemStyles;component/ButtonStyles.xaml"/>
            <ResourceDictionary 
                Source="pack://application:,,,/Selen.Wpf.SystemStyles;component/TextBoxStyles.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

 

Bei Verwendung explizit referenzieren:

<Button Content="blastallenemies.cmd" Style="{StaticResource LinkButton}"/>

 

Liste der Styles (Typ und Key)

TargetType Key
Button StandardButton
Button LinkButton
TabControl StandardTabControl
Menu StandardMenu
ListBox StandardListBox
ScrollBar StandardScrollBar
TextBox StandardTextBox
TextBox SearchTextBox

Download

Direkter Download

Codeproject Artikel

Neue Version vom Rehosted Workflow Designer

Share

Die neue Version von meinem dynamischen Rehosted Workflow Designer steht auf codeproject zum Download. Die wichtigsten Neuerungen sind neben einigen Bug Fixes und Refraktorisierungen vor allem UI Verbesserungen.

Select Workflow Type Dialog

Den Dialog zur Typauswahl habe ich komplett überarbeitet. Der Style ist dunkler geworden und die ListBox an der linken Seite hat ein schickes TreeView ersetzt. Damit ist es jetzt möglich die Workflowtypen in Gruppen einzuteilen und so anzuzeigen. IWorkflowType leitet dazu von dem Interface IAppTreeDataProvider ab, welches relevante Daten für das TreeView bereitstellt.

public interface IAppTreeDataProvider
{
     string Name { get; }
     string Path { get; }
     // ..
}

Fehleranzeige

Die Fehleranzeige für ungültiges Xaml habe ich überarbeitet. Neben der Anzeige von Zeilennummern in der TextBox kann sich der Balken zum Anzeigen der Exceptions an längere Texte anpassen und in der Größe variieren.

Download: Hier nochmal der Link zum codeproject Artikel, oder hier der direkte Download von meinem FTP  Server.

Dynamischer Rehosted Workflowdesigner für die WF 4 – Teil 2

Share

Rückblick auf das Ziel von Teil 1: Die Vorteile der Prism-Architektur sind:

  • Möglichkeit die Prism-Module in eine bestehende Anwendung einzubauen
  • Möglichkeit nur die benötigten Module zu verwenden
  • Möglichkeit die Module beliebig anzuordnen

Nachdem es also das letzte Mal um die grundsätzliche auf Prism-Modulen basierende Infrastruktur ging, möchte ich nun ein spezielleres Modul vorstellen, das sich um Speicher- und Ladevorgänge kümmert. Natürlich soll man hier dynamisch die Datenquelle ändern können, also z. B. entscheiden können, ob man den Workflow aus einer Datenbank oder aus einer Datei liest. Sinnvoll ist es natürlich auch mehrere solche Speicheroptionen zur Verfügung zu stellen. Ich habe mich dafür entschieden ein Dialogfeld zu implementieren, was die Speicheroptionen anzeigt und aus dem heraus man neue Workflows erstellen oder andere laden bzw. löschen kann. Zu bedenken ist auch, dass beim Erstellen eines neuen Workflows verschiedene Root-Activities verwendet werden können sollen und damit auch die Toolbox abhängig vom “Workflow-Typ” aufgebaut werden muss. Es ergibt sich damit das folgende Interface für  unsere Workflow-Typdefinition:

public interface IWorkflowType
{
    IToolboxCreatorService ToolboxCreatorService { get; }
    IStorageAdapter StorageAdapter { get; }
    string DisplayName { get; }
    string Description { get; }
}

Neben einer Beschreibung  und  einem Namen verweist der Workflowtyp auf zwei andere Interfaces, die Toolboxaufbau und Datenzugriffe kapseln. Das Interface für den Toolboxaufbau ist recht einfach, es liefert einfach eine Liste der Kategorien, die anzuzeigen sind. Im vorigen Artikel hatte ich ja schon das ToolboxModul erwähnt, das sich um das Laden der Toolbox kümmert. Dieses Modul nimmt später einfach wieder den ToolboxCreatorService entgegen.

Das Interface für die Datenzugriffe lässt sich in 2 Bereiche einteilen:

  • Abfrage von Name + Beschreibung aller im Datenspeicher vorhandenen Workflows
  • Abfragen der RootActivity und weiterer typspezifischer Daten

Der Grund für die Trennung ist natürlich die Performance. Es würde ewig dauern für alle Workflows ständig den Activity-Baum zu laden, zumal dieser ja zunächst noch aus XAML erzeugt werden muss. Workflow Description (Name + Beschreibung) und DesignerModel (RootActivity + weitere Daten) lassen sich über einen eindeutigen Schlüssel zuordnen, der abhängig von den Daten im DesignerModel ermittelt werden kann (wird z.B. dann beim Speichern ausgelesen).

public interface IStorageAdapter
{
    IEnumerable<IWorkflowDescription> GetAllWorkflowDescriptions();

    IDesignerModel CreateNewDesignerModel();
    bool CanCreateNewDesignerModel { get; }
    IDesignerModel GetDesignerModel(object key);
    bool SaveDesignerModel(IDesignerModel designerModel);
    bool DeleteDesignerModel(object key);
}

Hier mal ein Screenshot vom dazugehörigen Dialog:

Zusammengehalten und gesteuert wird das Ganze von einem eigenen Modul, was das Menüband oben erweitert. Durch WPF-Commands werden die Aktionen in zwei zentrale ViewModels weitergeleitet, die nach einigen Validierungen wieder die Interfaces aufrufen. Im nächsten Teil geht es dann um eine konkrete Implementierung der Interfaces.

Sourcecode:

Den kompletten (weiterentwickelten) Quellcode gibt es wieder hier.

Dynamischer Rehosted Workflowdesigner für die WF 4 – Teil 1

Share

Frohe Weihnachten! Und hier auch gleich mein kleines Weihnachtsgeschenk:

Ein immer wiederkehrendes Problem mit eigenen Anwendungen basierend auf der Workflow Foundation, ist die eigene Implementierung eines Workflowdesigners. Dafür stellt das .Net Framework einige Klassen zur Verfügung, die auf MSDN kurz erklärt werden.

Eigentlich ist es nutzlos, für jede Anwendung ein neues WPF-Projekt für den Workflow-Designer zu erstellen, weil sich eigentlich kaum etwas ändert. Zusätzlich muss man sich immer noch um eine Lösung für Speichern/Laden (Datenbank, Filesystem …) Gedanken machen, was ja auch immer gleich ist. Sinnvoll wäre es eine dynamische Standardanwendung zu haben, die man auf den jeweiligen Anwendungsfall noch ein bisschen anpassen kann. Als Lösung für diese immer wiederkehrende nervige Sache bietet sich eine modulare Implementierung an. Dabei sollte es möglich sein frei zu entscheiden wie flexibel man sein möchte, also wie viel Logik man aus der Infrastruktur übernimmt.

Also, genug rumgeschwafelt, jetzt kommt meine Implementierung. Ich hab mir gedacht, weil es ein größeres Projekt ist ein paar Artikel dazu zu schreiben. In diesem Artikel soll es zunächst um die grobe Struktur und um die “Core-Module” meiner Implementierung gehen, bevor wir dann später zum einzelnen optionalen Aufgaben kommen. Vorher aber trotzdem mal ein Screenshot:

dynamischer Workflowdesigner
Workflowdesigner

Meine Implementierung basiert auf Prism, obwohl ich eigentlich nur den RegionManager davon verwende. Mein Ziel, was ich damit erreicht habe, war es nicht vorzugeben, dass die Toolbox links ist oder so, sondern eben einfach eine Toolbox da ist, die dann der RegionManager an eine beliebige Stelle platziert. Damit sind die Kernkomponenten von meinem kleinen Framework ganz schlank und schlicht. Es gibt:

  • Ein zentrales ViewModel, das die Klasse WorkflowDesigner zur Verfügung stellt und sich um Dinge wie das Aktualisieren der Designfläche, oder Designerfehler usw. kümmert
  • Zwei zentrale Views (Toolbox, Designfläche und PropertyInspector) mit diesem ViewModel als DataContext
  • Ein weiteres zentrales ViewModel für die Toolbox (dazu später mehr)
  • Eine zentrale View mit diesem ViewModel als DataContext

Die Views beinhalten ledeglich jeweils ein Content-Control, dass an die jeweilige Property von der WorkflowDesigner Instanz bzw. eben an die Toolbox gebunden ist. Damit ist der WorkflowDesigner schön modular und nicht mehr alles in einer Klasse zentriert. Hier mal als Beispiel einen Ausschnitt vom StandardDesignerViewModel (DesignView und PropertyInspector) mit der Initialisierungslogik:

public class StandardDesignerViewModel : INotifyPropertyChanged, IDesignerViewModel
{
    public WorkflowDesigner Designer
    {
        get;
        set;
    }

    public void ReloadDesigner(Activity root)
    {
        this.Designer = new WorkflowDesigner();
        (new DesignerMetadata()).Register();
        this.Designer.Load(root);
    }
}

Die Views sehen alle gleich aus, z. B. so die Design-View:

<UserControl x:Class="RehostedDesigner.Designer.ViewModule.StandardView"
             ...>
    <ContentControl Content="{Binding Designer.View}"/>
</UserControl>

Die einzige größere Herausvorderung war hier das Laden der Bildchen für die Toolbox, dazu vielleicht mal in einem anderen Artikel. Grundsätzlich kann man über ein Interface steuern, welche Elemente gerade in der Toolbox angezeigt werden und mein ViewModel versucht dann die Icons automatisch zu finden (wer möchte schaut sich einfach den Quellcode an).

Zusammensetzen kann man das ganze dann mit einem Prism-Bootstrapper. Der Name klingt gewaltiger als was dahintersteckt: Eine kleine Klasse, die anstatt des MainWindows von der App.xaml aufgerufen wird:

protected override void OnStartup(StartupEventArgs e)
{
    new Bootstrapper().Run();
}

Die müssen wir von UnityBootstrapper ableiten und setzen damit den ganzen Apparat von Prism in Gang. Die Implementierung ist sehr standardmäßig, wenn ich mir meinen Bootstrapper so anschaue kommt es mir fast so vor, als hätte ich ihn irgendwo von der MSDN Hilfe kopiert, ist zwar absolut korrekt, aber absolut nicht hilfreich/aufschlussreich/sinnvoll? … naja ich kopiers trotzdem mal rein:

public class Bootstrapper : UnityBootstrapper
{
    protected override DependencyObject CreateShell()
    {
        MainWindow shell = new MainWindow();
        shell.Show();

        return shell;
    }

    protected override IModuleCatalog CreateModuleCatalog()
    {
        return new ConfigurationModuleCatalog();
    }

    protected override void ConfigureContainer()
    {
        base.ConfigureContainer();
        ((UnityConfigurationSection)ConfigurationManager.GetSection("unity")).Configure(this.Container);
    }
}

Natürlich brauchen wir jetzt noch unser MainWindow, in dem wir die Regions für den RegionManager definieren. Eigentlich ziemlich selbsterklärend (Standardanordung Toolbox links, PropertyInspector rechts):

<Window x:Class="Selen.WorkflowDesigner.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Selen.WorkflowDesigner"
        xmlns:regions="clr-namespace:Microsoft.Practices.Prism.Regions;assembly=Microsoft.Practices.Prism" 
        Title="Workflow Designer" Height="350" Width="525" WindowState="Maximized">
    <DockPanel>
        <Menu DockPanel.Dock="Top" regions:RegionManager.RegionName="StorageRegion"/>
        <StatusBar DockPanel.Dock="Bottom" Height="25">
            <StatusBarItem HorizontalAlignment="Right">
                <Image Source="logo.png" HorizontalAlignment="Right"/>
            </StatusBarItem>
        </StatusBar>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="4*"/>
                <ColumnDefinition Width="2*"/>
            </Grid.ColumnDefinitions>
            <ContentControl Grid.Column="0" Grid.Row="0" 
                            regions:RegionManager.RegionName="ToolboxRegion" Margin="5"/>
            <ContentControl Grid.Column="1" Grid.Row="0" 
                            regions:RegionManager.RegionName="DesignerViewRegion" Margin="5"/>
            <ContentControl Grid.Column="2" Grid.Row="0" 
                            regions:RegionManager.RegionName="DesignerPropertyInspectorViewRegion" Margin="5"/>
            <GridSplitter Grid.Column="0" HorizontalAlignment="Right" .../>
            <GridSplitter Grid.Column="1" HorizontalAlignment="Right" .../>
        </Grid>
        <DockPanel.Background>
            <LinearGradientBrush>
                <GradientStop Color="Black" Offset="0"/>
                <GradientStop Color="Gray" Offset="0.5"/>
                <GradientStop Color="Black" Offset="0.7"/>
                <GradientStop Color="Gray" Offset="1"/>
            </LinearGradientBrush>
        </DockPanel.Background>
    </DockPanel>
</Window>

Das wars dann für den ersten Teil. Als nächstes stehen weitere Module auf dem Plan, die optionale Funktionalität übernehmen können (Speichern u.ä.), inzwischen gibts aber schonmal den gesamten Quellcode des Projekts zum Download: Download hier

WCF Service auf Somee.com

Share

Vor ein paar Monaten habe ich einen kleinen Artikel über Webhosting mit Somee geschrieben und heute einen Kommentar dazu bekommen. Da ich ja nicht besonders viele richtige Kommentare kriege (die traurige Bilanz steht bei 1270 Spam Kommentaren, ein Glück, dass ich jetzt den Spam-Filter hab, sonst wäre wahrscheinlich mein Hotmail Account von den ganzen Benachrichtigungen übergelaufen, und 16 Genehmigten wovon über die Hälfte Trackbacks von mir selber sind), hab ich kein Problem damit mal einen Freitag Abend dafür zu nehmen.

Also, Marek will ein praktisches Beispiel, zu Befehl, los gehts:

Wir melden uns bei somee.com an (s. der verlinkte Artikel von oben).

Wenn alles geklappt hat, müssen wir noch ein paar Parameter für unsere neue Seite einstellen, interessant ist eigentlich nur die Asp.Net Version. Ich nehme jetzt mal an Marek programmiert mit dem Net Framework 4.0, also mach ich das so.image

Dann sind wir soweit und können Visual Studio aufmachen. Ich will Marek nicht gleich mit einer komplizierten WCF-Implementierung über den Haufen rennen also nehmen wir denke ich einfach das Template von Visual Studio für eine WCF Service Application

image

Sollte dann ungefähr so aussehen:

image

Wir lassen den Service also einfach genau so, wie er ist und veröffentlichen ihn auf unserer Website über das Publish Menü von Visual Studio (man kann die Dateien natürlich auch so hochkopieren).

image

Wichtig ist hier die Sache mit der FTP-Adresse, die bei somee immer noch mal ein “www.xxx.somee.com” hintendran braucht, bei mir wäre also die komplette Adresse ftp://selensoft.somee.com/www.selensoft.somee.com/.

Wir schauen nochmal über die Metadaten-Adresse nach ob alles geklappt hat:

image

Und jetzt schreiben wir noch eine kleine Test-Console. Neues Konsolenprojekt, Referenz auf System.ServiceModel und die Service-Dll hinzufügen, App Config hinzufügen.

Die Config ist ganz einfach, ein Endpoint mit unserer Service-Adresse, dem Contract aus der referenzierten Service Dll und dem Binding, was (weil ja Standard) das basicHttpBinding ist:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <system.serviceModel>
 <client>
 <endpoint name="TestService"
 binding="basicHttpBinding"
 address="http://selensoft.somee.com/Service1.svc"
 contract="WcfOnSomee.IService1"/>
 </client>
 </system.serviceModel>
</configuration>

 

Dann können wir diesen Endpoint mit einer einfachen DuplexFactory benutzen (Program Klasse):

static void Main(string[] args)
{
 IService1 proxy = new ChannelFactory<IService1>("TestService").CreateChannel();
 Console.WriteLine(proxy.GetData(123));
 (proxy as ICommunicationObject).Close();
 Console.ReadLine();
}

Und das war´s auch schon, es funktioniert!

image

PS: Wenn es nicht funktioniert oder ich etwas vergessen habe: Unbedingt Kommentar schreiben ;)

Rechenleistung auf Befehl – WCF Cluster

Share

Hier hatte ich mir schon mal ein paar Gedanken zu Clustern und Rechenleistung gemacht. Jetzt hab ich die Idee ein bisschen ausgebaut und einen ersten Entwurf mit WCF umgesetzt.

1. Die Architektur

Ein Cluster baut sich aus drei Teilen auf, dem Client (fordert die Abarbeitung einer Aufgabe an), dem Distributor (verteilt die Aufgaben) und dem Calculator (arbeitet die Aufgaben tatsächlich ab). Dabei kann es beliebig viele Clients und Calcultors geben, aber (erst mal) nur einen Distributor.

Der Reihe nach passiert dann folgendes:

  1. Der Distributor startet
  2. Ein Calculator startet und verbindet sich mit dem Distributor
  3. Ein Client startet und übergibt dem Distributor eine Aufgabenstellung
  4. Der Distributor leitet die an den Calculator weiter
  5. Der Calculator erledigt die Aufgabe und schickt das Ergebnis direkt an den Client zurück

Aus Performancegründen schickt der Calculator sein Ergebnis direkt zum Client zurück.

cluster

2. Die Implementierung

Die Implementierung basiert auf WCF. Jeder der drei Teilnehmer ist ein eigenständiger WCF-Service. Das ist nötig, denn Streaming (was ich hier für nötig halte, weil man ja auch mal ein paar größere Aufgaben erledigen will) läuft über einen eigenen WCF-Transport Channel, wir können also keine Callbacks verwenden. Der Vorteil ist, dass der Client einfach seine Service-Adresse als string an den Distributor und der an den Calculator übermitteln kann und dann die direkte Verbindung von Calculator zu Client ganz einfach hergestellt werden kann. Ok, ich hab gesagt ich hab einen Entwurf, also Code, hier ist er:

 1: ComputingPowerConsumer powerConsumer = new ComputingPowerConsumer();
 2: powerConsumer.CommandExecuted += (operationID, output) =>
 3: {
 4:     Console.WriteLine(output);
 5: };
 6:
 7: powerConsumer.ExecuteCommand(SayHelloCommand.SayHelloCommandID, Guid.NewGuid().ToString(), null);
 8: Console.ReadLine();

Das ist alles, was nötig ist um einen Command/Aufgabe ans Cluster abzusetzen.

  1. Einen neuen PowerConsumer erstellen (Helper Klasse von mir)
  2. Auf das Event registrieren, wenn die Aufgabe fertig ist
  3. Aufgabe abschicken, alles was man braucht ist
    1. Eine Command ID (später mehr dazu)
    2. Eine Operation ID (wird einfach überall durchgeschleift und kommt am Ende im Event wieder rein, damit man weiß zu welchem Aufruf man gerade was empfängt)
    3. Ein object als Parameter, das dem Command zur Ausführung übergeben wird

Ja und was ist nun dieser Command? Eigentlich nur eine Klasse, die von IComputingPowerCommand (noch ein Baustein von mir) ableitet:

 1: public class SayHelloCommand : IComputingPowerCommand
 2: {
 3:     public const string SayHelloCommandID = "{D89930D2-06CD-4B9B-B09A-AE54CF7DD818}";
 4:
 5:     public string CommandID
 6:     {
 7:         get
 8:         {
 9:             return SayHelloCommandID;
 10:         }
 11:     }
 12:
 13:     public object Execute(object input)
 14:     {
 15:         return "Hello World!";
 16:     }
 17: }

Jeder Command hat eine Execute Methode, die das object vom Clientaufruf reingereicht bekommt und eine eindeutige ID, die der Client im Aufruf mitgibt. Die DLL in der sich der Command befindet muss in jedem bin-Verzeichnis von jedem Calculator sein, und das wars auch schon!

Das einzige was noch fehlt ist die Angabe der Distributor-Adresse in der App-Config und fertig ist der Cluster Client:

 1: <system.serviceModel>
 2:     <client>
 3:         <endpoint name="SRSIPowerConsumerDistributor1"
 4:                     address="net.tcp://winwf7:8002/DistributorService"
 5:                     binding="netTcpBinding"
 6:                     bindingConfiguration="streamedTcp"
 7:                     contract="Selen.Clustering.PowerDistributor.IPowerConsumerDistributor"/>
 8:     </client>
 9:     <bindings>
 10:         <netTcpBinding>
 11:             <binding name="streamedTcp" transferMode="Streamed">
 12:                 <reliableSession enabled="false" ordered="false"/>
 13:                 <security mode="None"/>
 14:             </binding>
 15:         </netTcpBinding>
 16:     </bindings>
 17:     <behaviors>
 18:         <serviceBehaviors>
 19:             <behavior name="IComputingPowerDistributorBehaviour">
 20:                 <serviceMetadata/>
 21:                 <serviceDebug includeExceptionDetailInFaults="true"/>
 22:             </behavior>
 23:         </serviceBehaviors>
 24:     </behaviors>
 25: </system.serviceModel>

Wer sich hier die Config-Datei mal genau anschaut sieht sicherlich den komischen Namen vom Distributor-Endpoint (“SRSIPowerConsumerDistributor1”), das hängt damit zusammen, dass die Verbindung zum Distributor intern über ein weiteres kleines WCF-Framework von mir aufgebaut wird, das es ermöglicht mehrere Endpoints anzugeben zwischen denen dann im Fall eines Fehlers automatisch umgeschaltet wird um eine hohe Ausfallsicherheit zu gewährleisten.

Leider bin ich noch nicht ganz so weit das ganze Framework zu veröffentlichen, in ein paar Tagen, gibt´s dann aber das Selen.Clusteing Framework 1.0 und noch einen Artikel zum Thema WCF und Ausfallsicherheit.

Images in Silverlight – Effects und Reflection

Share

Seit heute steht meine neue Silverlight-Webseite online. Das Design ist ziemlich cool und hat auch ein bisschen Arbeit gemacht. Dabei sind ein paar Bilder, denen ich Effekte verpasst habe, hier mal zwei schöne Möglichkeiten das UI einer Silverlight-Webseite mit Images zu verschönern:

1. Einfache Effects definieren

Über die Effect Eigenschaft (die man natürlich auch auf anderen UI Elementen genauso definieren kann) ist es ganz einfach möglich einem Image zum Beispiel einen Schatten anzuhängen. Eine schöne dezente Konfiguration, die ich oft verwendet habe ist die:

 1: <Image Source="rss.png" Height="35" Margin="2">
 2:     <Image.Effect>
 3:         <DropShadowEffect Color="Gray" Opacity="0.6"/>
 4:     </Image.Effect>
 5: </Image>

Ohne Effect:

without

Mit Effect:

shadow

Je nach Anwendungsfall (und Größe vom Bild) macht es natürlich Sinn auch die anderen Eigenschaften von DropShadowEffect noch zu setzen (z.B. ShadowDepth um den Schatten weiter nach unten zu setzten usw.), ist aber in den meisten Fällen denke ich gar nicht nötig.

Der andere Effect, den Silverlight noch bereitstellt, ist der BlurEffect. Den finde ich allerdings nicht so hübsch und nur selten passend und hab ihn deshalb auch nicht eingesetzt. Man kann hier nur den Radius der Verwischung festlegen, für das gleiche Beispiel sähe das dann so aus:

 1: <Image Source="rss.png" Height="35" Margin="2">
 2:     <Image.Effect>
 3:         <BlurEffect Radius="5"/>
 4:     </Image.Effect>
 5: </Image>

Ohne Effect:

without

Mit Effect:

blur

2. Images spiegeln

Bis jetzt war der XAML Code ja eigentlich recht simpel, beim Spiegeln müssen wir uns jetzt schon ein paar mehr Gedanken machen. In WPF gibts für solche Fälle den VisualBrush, leider nicht in Silverlight, deshalb sind wir hier gezwungen zwei Bilder anzulegen, ein Originalbild und ein gespiegeltes, die wir dann zusammen in ein ItemsControl (z.B. StackPanel) packen:

 1: <StackPanel>
 2:     <Image Source="nn.png" Width="150"/>
 3:     <Image Source="nn.png" Width="150">
 4:         <Image.RenderTransform>
 5:             <TransformGroup>
 6:                 <ScaleTransform ScaleY="-1"/>
 7:                 <TranslateTransform Y="140"/>
 8:             </TransformGroup>
 9:         </Image.RenderTransform>
 10:         <Image.OpacityMask>
 11:             <LinearGradientBrush StartPoint="0.5, 1" EndPoint="0.5, 0">
 12:                 <GradientStop Offset="0" Color="#FFFFFFFF"/>
 13:                 <GradientStop Offset="0.5" Color="#22FFFFFF"/>
 14:                 <GradientStop Offset="0.65" Color="#00FFFFFF"/>
 15:             </LinearGradientBrush>
 16:         </Image.OpacityMask>
 17:     </Image>
 18: </StackPanel>

Das Originalbild ist natürlich ganz simpel, das gespiegelte Bild definiert 2 interessante Dinge:

  1. Eine RenderTransform: Verschiebt / Streckt / Zerrt … das Objekt ohne Rücksicht auf seine Umgebung (in WPF gibt es noch eine LayoutTransform, die vorher alles, was im Weg ist zur Seite schiebt). Die RenderTransform skaliert das Image erst einmal mit dem Faktor –1 (entspricht einer Spiegelung an der X-Achse), dann verschieben wir es mit Translate nach unten, damit es unser erstes Bild nicht überdeckt.
  2. Natürlich wollen wir, dass das ganze auch ein bisschen realistisch rüberkommt, das gespiegelte Bild soll also nach unten hin nach und nach immer mehr verschwinden. Das erreichen wir über eine OpacityMask, die über einen Farbverlauf definiert wird. Natürlich sind das hier keine wirklichen Farben, sondern es wird nur der Alpha-Wert (also der erste Wert) der Farbe verwendet, der die Durchsichtigkeit angibt. Bei mir variiert der hier von FF (voll sichtbar) bis 00 (voll durchsichtig).

Und hier das Resultat:

reflection