OpenMacroBoard / StreamDeckSharp

A simple .NET wrapper for Stream Deck
MIT License
351 stars 47 forks source link

Cannot Open Device #22

Closed GerritRie closed 4 years ago

GerritRie commented 4 years ago

I programmed an App for a Database Filling tool. On the PC if bild the Solution everything runs cool. if i now try to run it on an other Mashine it wont open then Streamdeck.

Maybe u can help me...

It would be soo great

wischi-chr commented 4 years ago

Could you provide a link to the code/project that doesn't work or send screenshots from the error message.

GerritRie commented 4 years ago

Where should i Upload it ? We Transf ?

GerritRie commented 4 years ago

And is it Possible to write a Text on a Key ?

wischi-chr commented 4 years ago

Could you pack the source (in a ZIP for example), upload it (e.g. https://send.firefox.com/) and send me the link either here or per mail to christian.wischenbart@gmail.com (if you don't want other people to download it)

GerritRie commented 4 years ago

Hey Chrstian are u German ? Cause of ur name ?

wischi-chr commented 4 years ago

Not German exactly, I'm from Austria.

wischi-chr commented 4 years ago

Klar wir können auch auf Deutsch weiter machen ^^. Hab's heruntergeladen, d.h. kannst es wieder raus nehmen.

Ich schau mal ob ich es builden kann und ob der Fehler bei mir auch auftritt.

GerritRie commented 4 years ago

Ich versuche gerade mehrere Streamdecks zu nutzen habe hier in dem Issue board einen Post gesehen wie du mehrere gleichzeitig nutzt. Wie kann ich jetzt nur ein ansprechen. Bzw Sortieren nach XL und Normal. Muss ich in jeder die Abfrage machen und die Connection öffnen ?

GerritRie commented 4 years ago

Klar wir können auch auf Deutsch weiter machen ^^. Hab's heruntergeladen, d.h. kannst es wieder raus nehmen.

Ich schau mal ob ich es builden kann und ob der Fehler bei mir auch auftritt.

Kann sein das dir dinge fehlen. Bin gerade wieder neu in C# eingestiegen, deswegen ist das projekt auch mehr als durcheinander xD

wischi-chr commented 4 years ago

Hab ohne Anleitung zwar das Spiel noch nicht ganz verstanden 😅 aber soweit klappt bei mir alles. Also ich kann es in Visual Studio starten und wenn ich die exe direkt starte klappt es auch.

Hab einen Punkt bei mir anpassen müssen (den Pfad zu den Bildern, da das hardcoded war) und ab und zu wenn ich "wild" herum drücke treten noch Exceptions aus das müsstest du dir anschauen.

Pass mal bei dir in Visual Studio die Exception settings an (Debug -> Windows -> Exception Settings) image

und die Execeptions solltest du so nicht catchen:

catch
{
    InitializeComponent();
    MessageBox.Show("No Streamdeck Found", "Error!");
    connected = false;
}

Das Problem hier ist, dass JEDE Exception (z.b. wenn ein Bild nicht geladen werden) als "No Streamdeck Found" angezeigt wird. Was natürlich nicht stimmt.

Tausch das mal für die Entwicklung durch das aus:

catch (Exception ex)
{
    InitializeComponent();
    MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace, "Error!");
    connected = false;
}

dann sollten bessere Fehlermeldungen angezeigt werden.

Das InitializeComponent(); darf nur an der ersten Zeile im Konstruktor stehen (also bei public Form1() die anderen Calls sollten alle entfernt werden, das wird nur 1x ganz am Anfang benötigt)

GerritRie commented 4 years ago

Wow mega krass danke dir !!! Das ist kein Spiel bzw es ist für ein Spiel und Zwar für Rainbow 6 Siege Tuniere. Da es keine Live API zu dem Spiel gibt sollen nachher die Spieler die gegeneinander antreten angezeigt werden und man damit quasi Live Statistiken zu dem Aktuellen Match erstellen. Das ist nur ein Kleiner Modul in einem anderen Modul steht eine Komplette MYsql Verarbeitungs Methode dahinter.

Das ist Komisch. Muss auf jedem PC wo ich das nutzen will die Treiber fürs Streamdeck haben oder wird das Streamdeck auch ohne treiber gefunden und angesprochen.

GerritRie commented 4 years ago

Hab einen Punkt bei mir anpassen müssen (den Pfad zu den Bildern, da das hardcoded war) und ab und zu wenn ich "wild" herum drücke treten noch Exceptions aus das müsstest du dir anschauen.

Kannst du mir das mal Zeigen XD habe das mit den Pfaden in Visualstudio und so noch nicht ganz genau verstanden xD ^^

wischi-chr commented 4 years ago

Du hast die Pfade "hardcoded", das bedeutet, dass er auf jedem Rechner die Bilder am gleichen Ort sucht. Bei dir im bitmap_handler auf Zeile 300 ca stehen alle Bilder mit "C:/Daten/Programme/Streamdeck_Handler/pics_streamdeck/icons/" wenn der Pfad zu den Bildern nicht bei jedem Rechner exakt gleich ist, dann geht sowas nicht, weil dann die Bilder nicht gefunden werden.

Lösung: Mit relativen Pfaden arbeiten. image

GerritRie commented 4 years ago

Krass danke !!!

Jetzt würde ich das ganze gerne auf zwei Streamdecks erweitern.

Jetzt habe ich mal einen ganz kleinen Simplen test gemacht. `using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using OpenMacroBoard; using OpenMacroBoard.SDK; using StreamDeckSharp;

namespace Streamdeck_Text_test { public partial class Form1 : Form { public Form1() { InitializeComponent(); int x = 0; var decks = StreamDeck.EnumerateDevices().ToList(); var decks_list = decks.OrderBy(o => o.DeviceName).ToList();

        Console.WriteLine(decks.Count);
        while (x <= decks.Count-1)
        {

            Console.WriteLine(decks_list[x].DeviceName);
            Console.WriteLine(decks_list[x].DevicePath);
            Console.WriteLine(decks_list[x].UseWriteCache);
            x++;
        }
        x = 0;

    }

}

}`

Damit bekomme ich jetzt gerade eine Sortierte Liste von Devices die Angeschlossen sind. Wie kann ich denn jetzt immer nur ein Deck ansprechen. Muss ich jetzt immer erst in Jeder Methode die auf ein Streamdeck zugreift auslesen welche connected sind und welche Seriennummer sie haben ? Da ja : var decks = StreamDeck.EnumerateDevices().ToList(); immer nur in einer Methode selber funktionier.

ansteuern kann ich sie ja dann mit var deck1 = decks[0].Open(); deck1.ShowLogo();

wischi-chr commented 4 years ago

And is it Possible to write a Text on a Key ?

Ja. Man kann in C# auf Bitmaps schreiben und dann das Bitmap für KeyBitmaps verwenden. z.B. so:

image

GerritRie commented 4 years ago

And is it Possible to write a Text on a Key ?

Ja. Man kann in C# auf Bitmaps schreiben und dann das Bitmap für KeyBitmaps verwenden. z.B. so:

image

Danke du bist ein Echtes Genie!!!! Danke dir !!!!!!!

GerritRie commented 4 years ago

Habe das mal Ausprobiert. Das geht soweit Klasse !!! Jetzt erstzt man damit ja das "Bild" gibt es auch eine Möglichkeit auf das Bild zu schreiben ??

wischi-chr commented 4 years ago

bezüglich mehrerer StreamDecks muss man sich nur ein paar Grundkonzepte verinnerlichen:

EnumerateDevices() holt eine Liste von angeschlossenen StreamDecks. Wenn man ein StreamDeck "steuern" möchte dann muss man wie du schon richtig gemacht hast mit Open() das Gerät öffnen (das kann man theoretisch mit so vielen Devices wie man möchte gleichzeitg machen)

Beendet wird die Steuerung nicht mit .ShowLogo(), sondern mit .Dispose() (https://docs.microsoft.com/en-us/dotnet/api/system.idisposable.dispose)

Die Frage mit der Seriennummer hab ich nicht ganz verstanden, aber wenn du die Seriennummer haben willst, dann einfach deck.GetSerialNumber();

gibt es auch eine Möglichkeit auf das Bild zu schreiben ??

Klar du musst nur vorher das Bild zeichnen und dann drüber schreiben. Zirka so:

image

GerritRie commented 4 years ago

Sry ja das mit Dispose(); weiß ich mit Show Logo war nur als Steuerbefehlt gedacht. Wenn Ich ja mehrere Classen mit mehreren Methoden habe. Muss ich ja sicher stellen das ich Quasi immer das streamdeck anspreche was ich auch wirklich will. Ich kann ja schlecht immer in jeder Methode erst die Liste Abfragen dann sortieren und dann hoffen das nachher immer als gleich Sortiert ist.

es kann ja passieren so war es eben das wenn ich Enumarte sie nicht immer in der Gleichen Reihenfolge sind. Also würde es ja Sinn machen sich alle Geräte auszulesen. Die Serien Nummer abzulegen. Und dann nur das Device öffnen mit der Passenden Serien Nummer.

wischi-chr commented 4 years ago

ok gehen wir es von der anderen Seite an. Was ist das Ziel? Soll man beim Starten eines auswählen können oder zeigt die Software (gleichzeitig) auf verschiedenen StreamDecks unterschiedliche Dinge an?

GerritRie commented 4 years ago

Also am Liebsten soll sie auf zwei Decks was unterschiedliches anzeigen. Nachher würde ich gerne eine Möglichkeit haben die Decks bzw den Inhalt auf den Decks zutauschen.

wischi-chr commented 4 years ago

Anhand eines Beispiels lassen sich Dinge meistens leichter erklären, daher hier ein Fallbeispiel:

Am Computer sind 10 StreamDecks angeschlossen, wir brauchen aber nur 2 (eines als Hauptscreen und eines für Stats beispielsweise)

//z.B:
var hauptScreen = decks[0];
var statusScreen = decks[1];
GerritRie commented 4 years ago

Gut das geht aber auch nur Solange ich alles in einer Methode mache. Wenn ich Quasi eine Aktion Auslage muss ich das Ganze wieder neu abfragen. Unbenannt

wischi-chr commented 4 years ago

deck1 und deck2 einfach als field speichern.

GerritRie commented 4 years ago

Also quasi : field deck1 = decks[0];

Wie gesagt bin leider noch etwas grün hinter den Ohren 😅

wischi-chr commented 4 years ago

Ja kein Problem wir haben alle mal am Anfang angefangen ^^

Wenn du eine Klasse hast (wie z.B. Form1 eine Klasse ist) dann kannst du "auf diese Klasse" Daten speichern. Fields sind eine Art Variablen die auf Klassen gespeichert werden und methodenübergreifend verwendet werden können.

public partial class Form1 : Form
{
    // these members are called "fields"
    private IStreamDeckBoard deck1;
    private IStreamDeckBoard deck2;

    private void StreamDeckInit()
    {
        // Get devices ... etc.
        deck1 = decks[0].Open();
        deck2 = decks[1].Open();
    }  

    private void MethodeA()
    {
        deck1.DoStuff();
        deck2.DoStuff();
    }

}
GerritRie commented 4 years ago

Perfekt Klasse danke !!

Ich habe aber noch ein Problem. Vlleicht kannst du mir da weiter helfen. Jedesmal wenn ich meine Software neu Starte Tauschen sich die Positionen der Decks in der Liste durch. Ich habe jetzt 1 Normales und Zwei XL Decks. Ich sortiere die Liste nach Namen der Devices.

Also:

Streamdeck Streamdeck XL Streamdeck XL

Jetzt tauschen sich nach jedem Neustart der Software die XL Decks 1 2

Hast du eine Idee Wie ich das Verhindern kann? 3

wischi-chr commented 4 years ago

Klar. Die originale Liste die von Windows ist nicht immer in der gleichen Reihenfolge, daher bekommst du hier unterschiede. Abhilfe würde es schaffen nicht nur nach dem Namen zu sortieren, sondern auch nach einem "Subkey".

Statt: decks.OrderBy(o => o.DeviceName).ToList()

Folgende Sortierung: decks.OrderBy(o => o.DeviceName).ThenBy(o => o.DevicePath).ToList()

Dann kommt es immer in der gleichen Reihenfolge.

GerritRie commented 4 years ago

Bleibt denn der Path immer gleich ? Sonst müsse ich ja immer beim Starten abfragen welches deck Welches ist macht das Sinn ?

Soll ich dir mal ein Bild machen wie es Physikalisch ist ?

von Links Nach Rechts: XL;Normal;XL => Deck2;Deck1;Deck3

GerritRie commented 4 years ago

.ThenBy(o => o.DevicePath).ToList()

Damit wäre es dann anders. Es wäre 3.1.2 ;(

wischi-chr commented 4 years ago

Der DevicePath wird von Windows versucht so "stabil" wie möglich zu halten (also dass er gleich bleibt) das ist i.d.R nur dann möglich wenn man die Geräte nicht zwischen den USB Ports wechselt.

Wenn es sehr stabil bleiben soll (also auch über neustarts hinweg) dann bleibt nichts anderes übrig als das was ich schon weiter oben mal erwähnt habe. Alle Devices mit Open aufmachen, die Seriennummer von allen Geräten lesen, dann die Geräte "rauspicken" die man haben will und die nicht benötigten Geräte disposen.

Hier eine Methode die genau das macht (mit Kommentaren damit es verständlicher ist was genau passiert ^^):

private static IList<IStreamDeckBoard> GetStreamDecks(params string[] serialnumbers)
{
    // Open all devices and store in a dictionary
    var openedDevices = StreamDeck
        .EnumerateDevices()
        .Select(d => d.Open())
        .ToDictionary(
            d => d.GetSerialNumber(),
            d => d
        );

    // we use this list to collect requested devices.
    var resultDevices = new List<IStreamDeckBoard>();

    // iterate over all requested serial numbers
    foreach (var serial in serialnumbers)
    {
        if (!openedDevices.ContainsKey(serial))
        {
            // if a requested deck is missing -> add null
            resultDevices.Add(null);
        }
        else
        {
            // add device with requested serial number
            resultDevices.Add(openedDevices[serial]);

            // and remove device from dictionary to prevent disposal
            openedDevices[serial] = null;
        }
    }

    // dispose devices that were not requested
    foreach (var device in openedDevices.Where(d => d.Value != null))
    {
        device.Value.Dispose();
    }

    return resultDevices;
}

Die Verwendung ist denkbar einfach, einfach die Methode aufrufen mit den Seriennummern der Decks die man haben will und in der Reihenfolge in der man die Decks haben will.

var decks = GetStreamDecks("CL18I1A00615", "AL15G1A00646");
// decks[0] ist das Gerät CL18I1A00615
// decks[1] ist das Gerät AL15G1A00646

Wenn man die Parameter dreht, dann kommen die Decks in umgekehrter Reihenfolge etc. Man kann auch mehr oder weniger Parameter angeben wenn man 3, 4, oder 5 Devices haben will.

GerritRie commented 4 years ago

Gail, das jedoch bedeut aber das ich immer Physikalisch die Gleichen Steamdecks haben muss richtig ??

Ich glaube dann muss ich mal sehen das mir eine Art Auswahl baue in denen Ich Quasi ein Zuordnung machen kann, falls etwas durcheinander Kommt

wischi-chr commented 4 years ago

Ja bei meinem Beispiel ist dass dann fix auf die harware gebunden, aber du könntest ja eine Kombination machen, also den User auswählen lassen welche Decks er verwenden will, dann die Seriennummern von den gewählten Decks speichern und beim nächsten Start genau die Decks wieder laden.