/* Code by calm-solutuions.de * Licensed under MIT License * Feel free to use this Software like free beer! * */ using System; using System.Collections.Generic; using System.Text; using System.IO.Ports; using System.IO; namespace eHajo_BlinkenLights { public partial class eHaJo_BL_API { public class pixel { public delegate void PixelChangedEventHandler(object sender); public event PixelChangedEventHandler OnPixelChangedEvent; protected void OnPixelChanged() { if (OnPixelChangedEvent != null) OnPixelChangedEvent(this); } private bool EventsAusloesen=true; public void SetPixel(bool pixel) { SetPixel(pixel, EventsAusloesen); } public void SetPixel(bool pixel, bool EventAusloesen) { AN_AUS=pixel; aEvt.Set(); if (EventAusloesen == true) { OnPixelChanged(); } } public bool GetPixel() { return AN_AUS; } public void MarkDirty() { aEvt.Reset(); } private bool AN_AUS; public System.Threading.AutoResetEvent aEvt = new System.Threading.AutoResetEvent(false); public void EnableEvent(bool Zustand) { EventsAusloesen = Zustand; } } public class BL8x8 { public delegate void BufferChangedEventHandler(object sender); public event BufferChangedEventHandler OnBufferChangedEvent; private bool EventsAusloesen = true; private ZeroCoordinate ZP = ZeroCoordinate.UpperRight; //0 Grad protected void OnBufferChanged() { if (OnBufferChangedEvent != null) OnBufferChangedEvent(this); } public void EnableEvent(bool Zustand) { EventsAusloesen = Zustand; } public BL8x8(UInt16 x, UInt16 y, ref pixel[,] Buffer) { this.x = x; this.y = y; this.Buffer = Buffer; for (uint xx = 0; xx < 8; xx++) { for (uint yy = 0; yy < 8; yy++) { Buffer[xx, yy] = new eHaJo_BL_API.pixel(); Buffer[xx, yy].OnPixelChangedEvent += new pixel.PixelChangedEventHandler(PixelChangedEvent); } } } private void PixelChangedEvent(object sender) { if (EventsAusloesen == true) { OnBufferChanged(); } } public void RiseBufferEvent() { OnBufferChanged(); } public readonly UInt16 x; public readonly UInt16 y; public pixel[,] Buffer =null; } public enum ZeroCoordinate { UpperLeft, UpperRight, LowerLeft, LowerRight }; private SerialPort CP = new SerialPort(); private enum BLFeedBack { ACK, NACK, TIMEOUT}; private Queue Rueckmeldung = new Queue(); private List Bildpuffer = new List(); private bool boFeedback = false; //Konstruktor der Klasse mit allen was der Mensch so braucht public eHaJo_BL_API(string COMPortName) { CP.PortName = COMPortName; CP.BaudRate = 9600; CP.DataBits = 8; CP.StopBits = StopBits.One; CP.Parity = Parity.None; CP.DataReceived += new SerialDataReceivedEventHandler(RX_DATA); RXTimer = new System.Timers.Timer(); RXTimer.Elapsed += new System.Timers.ElapsedEventHandler(CheckNewData); RXTimer.Interval = 25; } public void SetMatrixDimension(int x, int y) { X_Groesze = x; Y_Groesze = y; } private int X_Groesze = 0; private int Y_Groesze = 0; private System.Timers.Timer RXTimer = null; private bool Pollen = false; public void RX_Daten_Pollen(bool DatenPollen) { Pollen = DatenPollen; RXTimer.Enabled = DatenPollen; } //Einmal ein neues Blinkenlights registrieren lassen können public BL8x8 RegisterBL(UInt16 x, UInt16 y, ref pixel[,] Buffer) { bool found = false; BL8x8 nbl = null; foreach(BL8x8 BL in Bildpuffer) { if((BL.x==x)&&(BL.y==y)) { nbl= BL; break; } } if (found == false) { nbl = new BL8x8(x, y, ref Buffer); Bildpuffer.Add(nbl); return nbl; } else { return nbl; } } public pixel[,] GetBuffer(UInt16 PosX, UInt16 PosY) { BL8x8 result = Bildpuffer.FindLast( delegate(BL8x8 BL) { return ((BL.x == PosX) && (BL.y == PosY)); }); if (result != null) { return result.Buffer; } else { return null; } } public BL8x8 GetBL8x8(UInt16 PosX, UInt16 PosY) { BL8x8 result = Bildpuffer.FindLast( delegate(BL8x8 BL) { return ((BL.x == PosX) && (BL.y == PosY)); }); if (result != null) { return result; } else { return null; } } public void EnableBufferEvents(bool Zustand) { foreach (BL8x8 x in Bildpuffer) { x.EnableEvent(Zustand); } } public void RiseBufferEvents(bool Zustand) { foreach (BL8x8 x in Bildpuffer) { x.RiseBufferEvent(); } } /*---------------------------------------------------------------------------------------- * Funktion: SetPixle * Parameter: * Uint16 x , die X Koordinate des Pixel, startend oben links * Uint16 y , die Y Koordinate des Pixel, startend oben links * bool AN_AUS, setzt den passenden Wert des Pixel, true = an , false = aus * * Beschreibung: * Setzt einen Pixel in dem das passenden Datenpacket erstellt wird * * ---------------------------------------------------------------------------------------- */ public void SetPixel(UInt16 x, UInt16 y, bool Zustand) { //Okay einmal x und y in das passenden BL umrechnen UInt16 BLX = (UInt16)(x / 8); UInt16 BLY = (UInt16)(y / 8); UInt16 PosX = (UInt16)(x % 8); UInt16 PosY = (UInt16)(y % 8); //Zum Testen lokale x position spiegeln... PosX = (UInt16)(7 - PosX); SetPixleAtBL(BLX, BLY, PosX, PosY, Zustand); if(boFeedback==true) { BLFeedBack FB = DequeueWithTimeOut(ref Rueckmeldung, 250); if(FB==BLFeedBack.NACK) { //Da is was nicht so wie es soll..das BL kennt den Befel nicht, sollte aber nicht sein Console.WriteLine("Der Befel SetPixel(x,y,Zustand) wird von der Firmware nicht erkannt"); } else if (FB == BLFeedBack.TIMEOUT) { //Da is was nicht so wie es soll..das BL kennt den Befel nicht, sollte aber nicht sein Console.WriteLine("Der Befel SetPixel(x,y,Zustand) hat einen Timeput verursacht"); } } } public void SetPixleAtBL(UInt16 PosX, UInt16 PosY,UInt16 LocalX, UInt16 LocalY, bool AN_AUS) { //Okay mal sehen welche BL das ist wenn es der Master ist dann past das hier so if ((PosX == 0) && (PosY == 0)) { BL8x8 result = Bildpuffer.FindLast( delegate(BL8x8 BL) { return ((BL.x == PosX) && (BL.y == PosY)); }); if (result != null) { if ((LocalX < 8) && (LocalY < 8)) //Okay das Passt { byte Daten = 0; Daten += (byte)(LocalX << 4); Daten += (byte)(LocalY << 1); Daten += System.Convert.ToByte(AN_AUS); TX_DATA(Daten); result.Buffer[LocalX, LocalY].SetPixel(AN_AUS); } } } else { BL8x8 result = Bildpuffer.FindLast( delegate(BL8x8 BL) { return ((BL.x == PosX) && (BL.y == PosY)); }); if (result != null) { if ((LocalX < 8) && (LocalY < 8)) //Okay das Passt { byte Nummer = 0; Nummer = (byte)(X_Groesze * result.x + result.y + 1); //Dann einmal aus der Matrix das passenden BL raussuchen if (CP.IsOpen == true) { byte[] Daten = new byte[1]; Daten[0]=0x8A; System.Console.WriteLine("Steuerbyte: 0x{0:X}", Daten[0]); CP.Write(Daten, 0, 1); Daten[0] = Nummer; System.Console.WriteLine("Adresse: {0:D}", Daten[0]); CP.Write(Daten, 0, 1); byte Data = 0; Data += (byte)(LocalX << 4); Data += (byte)(LocalY << 1); Data += System.Convert.ToByte(AN_AUS); System.Console.WriteLine("Befehl: 0x{0:X}", Daten[0]); TX_DATA(Data); } result.Buffer[LocalX, LocalY].SetPixel(AN_AUS); } } } } /*---------------------------------------------------------------------------------------- * Funktion: GetPixle * Parameter: * Uint16 x , die X Koordinate des Pixel, startend oben links * Uint16 y , die Y Koordinate des Pixel, startend oben links * * Rückgabe: * bool, true = an und flase=aus * Beschreibung: * Liest einen Pixel in dem das passenden Datenpacket erstellt wird * * ---------------------------------------------------------------------------------------- */ public bool GetPixel(UInt16 x, UInt16 y) //Okay das hier wird häßlich da die Software alle Blinkenlighs als ein Image behandelt { //Okay einmal x und y in das passenden BL umrechnen UInt16 BLX = (UInt16)(x/8); UInt16 BLY = (UInt16)(y/8); UInt16 PosX = (UInt16)(x%8); UInt16 PosY = (UInt16)(y%8); //Dann mal sehen ob es da ein Blinkenlights gibt... BL8x8 result = Bildpuffer.FindLast( delegate(BL8x8 BL) { return ((BL.x == BLX) && (BL.y == BLY)); }); if (result != null) { byte Daten = 0xC0; Daten += (byte)(x << 3); Daten += (byte)(y); //Okay die Daten sind da also los result.Buffer[x, y].MarkDirty(); TX_DATA(Daten); //Update ist angefordert //Warten bis die neuen Daten da sind. if (result.Buffer[x, y].aEvt.WaitOne(125) == false) { //Okay da hat es einen Timeout gegeben Console.WriteLine("GetPixel hat ein Timeout verursacht..."); return false; } return result.Buffer[x, y].GetPixel(); } else { return false; } } /*---------------------------------------------------------------------------------------- * Funktion: GetImage * Parameter: * kein * * Rückgabe: * bool Array[x][y] , true = an und flase=aus * Beschreibung: * Liest das ganze Bild in dem das passenden Datenpacket erstellt wird * * ---------------------------------------------------------------------------------------- */ public bool[,] GetImage() { bool[,] Image = new Boolean[8, 8]; for (byte x = 0; x < 8; x++) { for (byte y = 0; y < 8; y++) { Image[x, y] = GetPixel(x, y); } } return Image; } public void ClearScreen() { TX_DATA((byte)(0x80 | 0x01)); BLFeedBack FB = DequeueWithTimeOut(ref Rueckmeldung, 250); if (FB == BLFeedBack.NACK) { //Da is was nicht so wie es soll..das BL kennt den Befel nicht, sollte aber nicht sein Console.WriteLine("Der Befel ClearScreen() wird von der Firmware nicht erkannt"); } else if (FB == BLFeedBack.TIMEOUT) { //Da is was nicht so wie es soll..das BL kennt den Befel nicht, sollte aber nicht sein Console.WriteLine("Der Befel ClearScreen() hat einen Timeput verursacht"); } } public void InvertScreen() { TX_DATA((byte)(0x80 | 0x02)); BLFeedBack FB = DequeueWithTimeOut(ref Rueckmeldung, 250); if (FB == BLFeedBack.NACK) { //Da is was nicht so wie es soll..das BL kennt den Befel nicht, sollte aber nicht sein Console.WriteLine("Der Befel InvertScreen() wird von der Firmware nicht erkannt"); } else if (FB == BLFeedBack.TIMEOUT) { //Da is was nicht so wie es soll..das BL kennt den Befel nicht, sollte aber nicht sein Console.WriteLine("Der Befel InvertScreen() hat einen Timeput verursacht"); } } public void SelectZeroCoordinate(ZeroCoordinate ZP) { switch (ZP) { case ZeroCoordinate.UpperLeft: { TX_DATA((byte)(0x84 | 0x01)); } break; case ZeroCoordinate.UpperRight: { TX_DATA((byte)(0x84 | 0x00)); } break; case ZeroCoordinate.LowerRight: { TX_DATA((byte)(0x84 | 0x03)); } break; case ZeroCoordinate.LowerLeft: { TX_DATA((byte)(0x84 | 0x02)); } break; default: { TX_DATA((byte)(0x84 | 0x00)); } break; } BLFeedBack FB = DequeueWithTimeOut(ref Rueckmeldung, 250); if (FB == BLFeedBack.NACK) { //Da is was nicht so wie es soll..das BL kennt den Befel nicht, sollte aber nicht sein Console.WriteLine("Der Befel SelectZeroCoordinate(ZP) wird von der Firmware nicht erkannt"); } else if (FB == BLFeedBack.TIMEOUT) { //Da is was nicht so wie es soll..das BL kennt den Befel nicht, sollte aber nicht sein Console.WriteLine("Der Befel SelectZeroCoordinate(ZP) hat einen Timeput verursacht"); } } //Routinen für den COMPort public void SetCOMPort(string Portname) { CP.PortName = Portname; } public bool PollenAktiv() { return Pollen; } public bool OpenCOMPort() { try { CP.Open(); return true; } catch { return false; } } public bool EnumerateBL () { try { byte[] Daten = new byte[1]; Daten [0] = 0x88; CP.Write (Daten, 0, 1); return true; } catch { return false; } } public bool CloseCOMPort() { try { CP.Close(); return true; } catch { return false; } } public bool IsComOpen() { return CP.IsOpen; } public string GetPortname() { return CP.PortName; } private void RX_DATA(object sender, SerialDataReceivedEventArgs e) { //Okay einmal das Ganze zerlegen lassen while (CP.BytesToRead > 0) { byte RX_B = (byte)CP.ReadByte(); if ((RX_B & 0x80)!=0) //Neues Pixel ist da { //KLann nur vom Master sein UInt16 PosX =0; UInt16 PosY =0; UInt16 x = (UInt16)((RX_B & 0x70)>>4); UInt16 y = (UInt16)((RX_B & 0x0E) >> 1); bool Pixel = System.Convert.ToBoolean(RX_B & 0x01); BL8x8 result = Bildpuffer.FindLast( delegate(BL8x8 BL) { return ((BL.x == PosX) && (BL.y == PosY)); }); if (result != null) { result.Buffer[x, y].SetPixel(Pixel); } } //Es gibt nun noch 0x08 und 0x15 für ACK und NACK else if (RX_B == 0x08) { //Es ist ein ACK gekommen Rueckmeldung.Enqueue(BLFeedBack.ACK); } else if (RX_B == 0x15) { //Es ist ein NACK angekommen Rueckmeldung.Enqueue(BLFeedBack.NACK); } } } private void TX_DATA(byte Daten) { //Einmal die Datensenden lassen if (CP.IsOpen == true) { byte[] Data = new byte[1]; Data[0] = Daten; try { if (Rueckmeldung.Count != 0) { Console.WriteLine("Eine nicht Behandelte Rückmeldung war noch vorhanden...:-("); } Rueckmeldung.Clear(); //Was auch immer da Drinne war ist nun unwichtig... CP.Write(Data, 0, 1); } catch (SystemException pex) { //Okay da is was hin... //Bei dem Blinkenlights kann es ein das der UART nen abgang macht } } } //Einmal ein Fix für das Pollen des Comport unter Linux private void CheckNewData(System.Object sender, System.Timers.ElapsedEventArgs e) { if (CP.IsOpen == true) { if (CP.BytesToRead != 0) { RX_DATA(null, null); } } } private BLFeedBack DequeueWithTimeOut(ref Queue Q, Int32 Timeout_ms) { while ((Q.Count == 0) && Timeout_ms > 0) { if (Timeout_ms > 50) { System.Threading.Thread.Sleep(50); Timeout_ms -= 50; } else { System.Threading.Thread.Sleep(Timeout_ms); Timeout_ms = 0; } } if (Q.Count != 0) { return Q.Dequeue(); } else { return BLFeedBack.TIMEOUT; } } } } 8)) //Okay das Passt { byte Daten = 0; Daten += (byte)(LocalX