Cm11LowLevelDriverWorker.cs :  » Build-Systems » CruiseControl.NET » ThoughtWorks » CruiseControl » CCTrayLib » X10 » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Build Systems » CruiseControl.NET 
CruiseControl.NET » ThoughtWorks » CruiseControl » CCTrayLib » X10 » Cm11LowLevelDriverWorker.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;

namespace ThoughtWorks.CruiseControl.CCTrayLib.X10{
    public partial class Cm11LowLevelDriver
    {
        /// <summary>
        /// Private classs run in separate thread to actually send commands on the wire
        /// </summary>
        private class Cm11LowLevelDriverWorker
        {
            /// <summary>
            /// Event is raised when an communication error occurrs. Message contains description of error.
            /// </summary>
            public event System.EventHandler<Cm11LowLevelDriverError> Error;

            private System.Collections.Generic.Queue<Cm11Message> queue = new System.Collections.Generic.Queue<Cm11Message>();
            private object queuePadLock = new object(); // Used to synclock queue interaction.
            private volatile bool stopWorker = false;  // Used to signal thread stop
            private SerialPort comm; //Serial Port CM11a Controller is connected to
            private CM11aHouseCode x10HouseCode; //House code to control
            private int transmissionRetries = 5; //Number of times to retry transmissions
            static readonly byte[] okBuffer = new byte[1] { 0x00 }; //Const for the OK command
            static readonly int comRetryInterval = 5000; // Milliseconds between trying to retrying to establish com communications

            private bool supressComErrors = false;    // Flag used to make sure comError events only are sent once
            private DateTime lastComTry = DateTime.Now.AddSeconds(-10); // Used to make sure we only retry ComCreation every 5 seconds

            private string comPort;
            private int baudRate;
            private Parity parity;
            private int dataBits;
            private StopBits stopBits;

            public Cm11LowLevelDriverWorker(CM11aHouseCode houseCode, string comPort, int baudRate, Parity parity, int dataBits,
                                  StopBits stopBits)
            {
                x10HouseCode = houseCode;
                // We defer creation of comPort until first communication attempt
                // until then, store settings, that way we will also be able to 
                // restore a comPort if configured ComPort is on a USB device that is
                // temporarily disconnected.
                this.comPort = comPort;
                this.baudRate = baudRate;
                this.parity = parity;
                this.dataBits = dataBits;
                this.stopBits = stopBits;
            }

            /// <summary>
            /// This method is the main method of the worker class.
            /// it polls the Queue every second and sends them to the
            /// CM11 interface.
            /// </summary>
            public void StartProcessing()
            {
                // This is the processing loop
                while (!stopWorker)
                {       
                    ProcessCommand();
                    
                    // Wait a half a second before checking the queue again.
                    System.Threading.Thread.Sleep(500);
                }

                // Close down Comms and quit thread
                this.CloseDriver();
                return;
            }

            /// <summary>
            /// Closes down communications and releases resources.
            /// </summary>
            public void CloseDriver()
            {
                if (comm != null)
                {
                    if (comm.IsOpen)
                        try
                        {
                            comm.Close();
                        }
                        catch (System.IO.IOException)
                        {
                            // Cannot shutwdown IO port
                            // doesnt really matter now, we are shutting down.
                        }
                    comm = null;
                }
            }

            /// <summary>
            /// Adds a message to the queue.
            /// </summary>
            /// <param name="message">Message to be added to the queue</param>
            public void AddMessage(Cm11Message message)
            {
                lock (queuePadLock)
                {
                    if (this.queue.Count < 100)
                    {
                        this.queue.Enqueue(message);
                    }
                    else
                    {
                        // We already have 100 commands waiting. 
                        // Just drop further commands. Comms are probably
                        // down. 
                        RaiseErrorEvent(new Cm11LowLevelDriverError("Communication to CM11 interface is probably down and message queue is full."));
                    }
                }
            }

            /// <summary>
            /// Call this method to stop the Worker.
            /// </summary>
            public void StopProcessing()
            {
                stopWorker = true;
            }

            /// <summary>
            /// Returns or Sets the number of communication retries.
            /// </summary>
            public int TransmissionRetries
            {
                get { return (transmissionRetries); }
                set { transmissionRetries = value; }
            }

            /// <summary>
            /// If commands are found in the queue it sends commands and removes processed commands from queue
            /// </summary>
            private void ProcessCommand()
            {
                // Check if we have any commands in the queue to process.
                if (queue.Count > 0)
                {
                    Cm11Message command = queue.Peek();
                    if (command != null)
                    {
                        // Try to send command to CM11 device
                        try
                        {
                            if (Send(command.Buffer, command.Count))
                            {
                                if (SendOk())
                                {
                                    lock (queuePadLock)
                                    {
                                        queue.Dequeue();
                                    }
                                }
                            }
                        }
                        catch (System.TimeoutException)
                        {
                            RaiseErrorEvent(new Cm11LowLevelDriverError("Sending command to CM11 interace failed. Is the CM11 interface still plugged in to the AC outlet?"));
                        }
                    }
                }
            }
            
            /// <summary>
            /// Checks if ComPort is created, if not it will try to create one.
            /// </summary>
            private bool ComPortCreated()
            {
                if (comm == null && DateTime.Now.Subtract(lastComTry).TotalMilliseconds> comRetryInterval)
                {                    
                    lastComTry = DateTime.Now;
                    try
                    {
                        comm = new SerialPort(comPort, baudRate, parity, dataBits, stopBits);
                        
                        // reset flag
                        supressComErrors = false;
                        lastComTry = DateTime.Now.AddSeconds(-10);
                    }
                    catch (System.UnauthorizedAccessException)
                    {
                        if(!supressComErrors)
                            RaiseErrorEvent(new Cm11LowLevelDriverError("Access error. " + comPort + " is probably already in use by another application. Will not send this message again."));

                        supressComErrors = true;
                        return false;
                    }
                    catch (Exception ex)
                    {
                        if(!supressComErrors)
                            RaiseErrorEvent(new Cm11LowLevelDriverError("An error occurred trying to initiate communications through " + comPort + " Message: " + ex.Message + " Will not send this message again"));

                        supressComErrors = true;
                        return false;
                    }
                }

                return true;
            }

            /// <summary>
            /// Checks if ComPort is open, if it is not will try to open it.
            /// </summary>
            /// <returns>True if ComPort is open for communication</returns>
            private bool CommsOpen()
            {
                // Can't proceed if ComPort is not created.
                if (!ComPortCreated())
                    return false;

                if (!comm.IsOpen && DateTime.Now.Subtract(lastComTry).TotalMilliseconds > comRetryInterval)
                {
                    lastComTry = DateTime.Now;
                    try
                    {
                        // Open Communications.
                        comm.ReadTimeout = 3000;
                        comm.Open();
                        // Reset com errors flag
                        supressComErrors = false;
                    }
                    catch (System.IO.IOException ioEx)
                    {
                        if(!supressComErrors)
                            RaiseErrorEvent(new Cm11LowLevelDriverError("Could not open communications: " + ioEx.Message));

                        supressComErrors = true;
                        
                    }
                    catch (System.UnauthorizedAccessException)
                    {
                        if (!supressComErrors)
                            RaiseErrorEvent(new Cm11LowLevelDriverError("Access error. " + comPort + " is probably already in use by another application."));

                        supressComErrors = true;
                    }
                    catch (System.TimeoutException)
                    {
                        if (!supressComErrors)
                            RaiseErrorEvent(new Cm11LowLevelDriverError("Time out trying to open " + comPort));

                        supressComErrors = true;
                    }
                }

                return comm.IsOpen;
            }

            /// <summary>
            /// Sends the X10 Command to the CM11a
            /// </summary>
            /// <param name="buffer">Command to send in an array of bytes</param>
            /// <param name="count">size of command array</param>
            private bool Send(byte[] buffer, int count)
            {
                if (!CommsOpen())
                {
                    return false;
                }
                int retVal = 0;
                int x = 0;
                do
                {
                    comm.DiscardInBuffer(); //Get rid of anything in the buffer
                    comm.Write(buffer, 0, count); //send transmission to device
                    retVal = comm.ReadByte();
                    if (0x5a == retVal)
                    { //If status messages are being transmitted cmd will not go through
                        byte[] sendpollack = new byte[1];
                        sendpollack[0] = 0xc3;
                        comm.Write(sendpollack, 0, 1); //Send cmd to acknowledged status command.
                    }
                    if (0xa5 == retVal)
                    { // Detected a Time Request message from interface and this is not in response 
                        // to us sending a timer download header message.
                        byte[] timerDownload = TimerDownloadMessage(DateTime.Now);
                        comm.Write(timerDownload, 0, timerDownload.Length);
                        int ignoreNum = comm.ReadByte();  // Ignore checksum
                        comm.Write(okBuffer, 0, 1);
                    }
                    x++;
                } while ((Checksum(buffer, count) != retVal) && this.TransmissionRetries > x);
                //Try to resend transmission of it did not go through

                //If too many retried raise error event
                if (this.TransmissionRetries <= x)
                {
                    RaiseErrorEvent(new Cm11LowLevelDriverError("Failed to communicate with X10 controller device"));
                    return false;
                }

                // Message sent!
                return true;
            }

            /// <summary>
            /// Sends Ok to CM11 interface
            /// </summary>
            /// <returns></returns>
            private bool SendOk()
            {
                return Send(okBuffer , 1);
            }

            /// <summary>
            /// Returns a complete Timer Download Message in response to a Time Request
            /// </summary>
            private byte[] TimerDownloadMessage(DateTime date)
            {
                // If Power-fail some interfaces will ask for date time update.
                // we will have to give them a new date and time or
                // transmitted cmds will not go through

                // Snippet from: http://www.heyu.org/docs/protocol.txt
                // For a CM11, the time request is from the interface is: 0xa5.
                // The PC must then respond with the following transmission
                // Bit range  Description
                // 55 to 48  timer download header (0x9b)              (byte 0)
                // 47 to 40  Current time (seconds)                    (byte 1)
                // 39 to 32  Current time (minutes ranging from 0 to 119)    (byte 2)
                // 31 to 24  Current time (hours/2, ranging from 0 to 11)  (byte 3)
                // 23 to 15  Current year day (MSB is bit 15)            (byte 4+.1)
                // 14 to 8  Day mask (SMTWTFS)                        (byte 5-.1)
                // 7 to 4  Monitored house code                    (byte 6...)
                // 3    Reserved
                // 2    Timer purge flag     
                // 1    Battery timer clear flag
                // 0    Monitored status clear flag
                // End of snippet

                int minute = date.Minute;
                int hour = date.Hour / 2;
                if (Math.IEEERemainder(date.Hour, 2) > 0)
                { // Add remaining minutes 
                    minute += 60;
                }

                int wday = Convert.ToInt16(Math.Pow(2, (int)date.DayOfWeek));
                int yearDay = date.DayOfYear - 1;
                if (yearDay > 255)
                {
                    yearDay = yearDay - 256;
                    // Set current yearDay flag in wday's 7:th bit, since yearDay overflowed...
                    wday = wday + Convert.ToInt16(Math.Pow(2, 7));
                }

                // Build message
                byte[] message = new byte[7];
                message[0] = 0x9b;   // Time download header
                message[1] = Convert.ToByte(date.Second);
                message[2] = Convert.ToByte(minute);
                message[3] = Convert.ToByte(hour);
                message[4] = Convert.ToByte(yearDay);
                message[5] = Convert.ToByte(wday);
                message[6] = Convert.ToByte(0x03 + (int)this.x10HouseCode); // Send timer purgeflag,Monitored status clear flag, monitored house code.

                return message;
            }

            /// <summary>
            /// Computes Checksum of transmission to determine if the controller received command
            /// properly
            /// </summary>
            /// <param name="buffer">Command to send in the form of a byte array.</param>
            /// <param name="count">length of command array</param>
            /// <returns></returns>
            private int Checksum(byte[] buffer, int count)
            {
                if (1 < count)
                {
                    byte iRetVal = 0;
                    foreach (byte element in buffer)
                    {
                        iRetVal += element;
                    }
                    return (iRetVal & (byte)0xFF);
                }
                else if (1 == count)
                {
                    if (0 == buffer[0])
                    {
                        return (0x55);
                    }
                }
                return (0x00);
            }

            /// <summary>
            /// Raises the Error event
            /// </summary>
            /// <param name="error"></param>
            private void RaiseErrorEvent(Cm11LowLevelDriverError error)
            {
                // If we have any subscribers, raise event.
                if (this.Error != null)
                    this.Error(this, error);

            }
        }
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.