/// <summary>
        /// Crea una nuova istanza della classe ProcessingServiceMonitor usando i parametri specificati
        /// per la configurazione di un proxy interno.
        /// </summary>
        /// <param name="bindingConfig">La configurazione del proxy interno.</param>
        /// <param name="remoteUri">L'uri del servizio di elaborazione.</param>
        /// <remarks>
        /// La configurazione del proxy interno è posticipata e viene pertanto eseguita non appena sarà
        /// richiesta la prima comunicazione col servizio di elaborazione.
        /// </remarks>
        public ProcessingServiceMonitor(Binding bindingConfig, Uri remoteUri)
        {
            m_BindingConfig = bindingConfig;
            m_RemoteUri = remoteUri;

            m_InternalProxy = null;
            m_MonitorClosed = false;
        }
        /// <summary>
        /// Determina l'avvio della procedura che permette di inviare il task specificato al servizio di elaborazione,
        /// di verificarne periodicamente il completamento e di eseguire alla fine il download dei relativi risultati
        /// eventualmente prodotti.
        /// </summary>
        /// <remarks>
        /// Prima di invocare questo metodo, è necessario utilizzare il metodo SetTaskData per poter impostare i dati
        /// relativi al task da elaborare.
        /// Inoltre, questo metodo non dovrebbe essere invocato sul thread della UI, in quanto la verifica periodica
        /// del completamento dell'elaborazione blocca tale thread per l'intervallo di polling specificato.
        /// </remarks>
        public void Start()
        {
            if (m_Proxy == null)
            {
                try
                {
                    RaiseTaskExecutionProgress(TaskRequestState.InitializingProxy, null);
                    m_Proxy = new ProcessingServiceClient(m_Binding, m_Endpoint);
                    m_Proxy.Open();
                    RaiseTaskExecutionProgress(TaskRequestState.ProxyInitialized, null);

                    RaiseTaskExecutionProgress(TaskRequestState.SendingRequest, null);
                    m_TaskRequestId = m_Proxy.SubmitData(m_TaskData);
                    RaiseTaskExecutionProgress(TaskRequestState.RequestSent, null);

                    bool completed = false;
                    while (!completed)
                    {
                        m_PollingWaitHandle.WaitOne(m_PollingInterval);
                        TaskState ts = m_Proxy.GetState(m_TaskRequestId);

                        switch (ts)
                        {
                        case TaskState.Completed:
                            RaiseTaskExecutionProgress(TaskRequestState.DownloadingResults, null);
                            m_TaskResults = m_Proxy.GetResults(m_TaskRequestId);
                            RaiseTaskExecutionProgress(TaskRequestState.ResultsDownloaded, null);
                            RaiseTaskExecutionCompleted(false, null, m_TaskResults);
                            completed = true;
                            break;

                        case TaskState.None:
                            completed = true;
                            break;

                        default:
                            completed = false;
                            break;
                        }
                    }

                    RaiseTaskExecutionProgress(TaskRequestState.DisposingProxy, null);
                    m_Proxy.Close();
                    RaiseTaskExecutionProgress(TaskRequestState.ProxyDisposed, null);
                }
                catch (Exception ex)
                {
                    HandleError(ex);
                }
            }
        }
        /// <summary>
        /// Istanzia un nuovo oggetto della classe TaskExecution con i parametri specificati.
        /// </summary>
        /// <param name="serviceUri">indirizzo del servizio di elaborazione del task</param>
        /// <param name="pollingInterval">tempo di attesa nella verifica di completamento del task</param>
        /// <exception cref="UriFormatException">
        /// Se si specifica un URI non valido come indirizzo del servizio di elaborazione.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// Se si specifica il valore null come indirizzo del servizio di elaborazione.
        /// </exception>
        public TaskExecution(string serviceUri, TimeSpan pollingInterval)
        {
            m_PollingInterval = pollingInterval;
            m_PollingWaitHandle = new ManualResetEvent(false);

            m_Binding = new BasicHttpBinding();
            m_Endpoint = new EndpointAddress(serviceUri);

            m_Proxy = null;
            m_CommunicationErrorsMapping = new Dictionary<Type, TaskExecutionState>()
            {
                { typeof(TimeoutException), TaskExecutionState.TimeoutError },
                { typeof(EndpointNotFoundException), TaskExecutionState.ServiceNotFoundError },
                { typeof(CommunicationException), TaskExecutionState.CommunicationError }
            };

            m_TaskRequestId = string.Empty;
            m_TaskData = new TaskData() { Name = string.Empty, Contents = string.Empty };
            m_TaskResults = null;
        }
        /// <summary>
        /// Istanzia un nuovo oggetto della classe TaskExecution con i parametri specificati.
        /// </summary>
        /// <param name="serviceUri">indirizzo del servizio di elaborazione del task</param>
        /// <param name="pollingInterval">tempo di attesa nella verifica di completamento del task</param>
        /// <exception cref="UriFormatException">
        /// Se si specifica un URI non valido come indirizzo del servizio di elaborazione.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// Se si specifica il valore null come indirizzo del servizio di elaborazione.
        /// </exception>
        public TaskExecution(string serviceUri, TimeSpan pollingInterval)
        {
            m_PollingInterval   = pollingInterval;
            m_PollingWaitHandle = new ManualResetEvent(false);

            m_Binding  = new BasicHttpBinding();
            m_Endpoint = new EndpointAddress(serviceUri);

            m_Proxy = null;
            m_CommunicationErrorsMapping = new Dictionary <Type, TaskRequestState>()
            {
                { typeof(TimeoutException), TaskRequestState.TimeoutError },
                { typeof(EndpointNotFoundException), TaskRequestState.ServiceNotFoundError },
                { typeof(CommunicationException), TaskRequestState.CommunicationError }
            };

            m_TaskRequestId = string.Empty;
            m_TaskData      = new TaskData()
            {
                Name = string.Empty, Contents = string.Empty
            };
            m_TaskResults = null;
        }
        /// <summary>
        /// Permette di gestire un errore verificatosi durante la procedura e restituisce true se è stata gestita,
        /// altrimenti false se non è stato necessario gestirlo poiché di fatto non è avvenuta alcun errore.
        /// </summary>
        /// <param name="error">l'eccezione che ha provocato l'errore</param>
        private void HandleError(Exception error)
        {
            if (error != null)
            {
                TaskRequestState state;
                if (m_CommunicationErrorsMapping.TryGetValue(error.GetType(), out state))
                {
                    RaiseTaskExecutionProgress(state, error);

                    m_Proxy.Abort();
                    RaiseTaskExecutionProgress(TaskRequestState.ProxyDisposed, error);

                    m_Proxy = null;
                }
                else
                {
                    if (error is FaultException <ServiceFault> )
                    {
                        RaiseTaskExecutionProgress(TaskRequestState.OperationError, error);

                        RaiseTaskExecutionProgress(TaskRequestState.DisposingProxy, error);
                        m_Proxy.Close();
                    }
                    else
                    {
                        RaiseTaskExecutionProgress(TaskRequestState.UnknownError, error);

                        m_Proxy.Abort();
                        RaiseTaskExecutionProgress(TaskRequestState.ProxyDisposed, error);

                        m_Proxy = null;
                    }
                }

                RaiseTaskExecutionCompleted(false, error, null);
            }
        }
        /// <summary>
        /// Permette di gestire un errore verificatosi durante la procedura e restituisce true se è stata gestita,
        /// altrimenti false se non è stato necessario gestirlo poiché di fatto non è avvenuta alcun errore.
        /// </summary>
        /// <param name="error">l'eccezione che ha provocato l'errore</param>
        private void HandleError(Exception error)
        {
            if (error != null)
            {
                TaskExecutionState state;
                if (m_CommunicationErrorsMapping.TryGetValue(error.GetType(), out state))
                {
                    RaiseTaskExecutionProgress(state, error);

                    m_Proxy.Abort();
                    RaiseTaskExecutionProgress(TaskExecutionState.ProxyDisposed, error);

                    m_Proxy = null;
                }
                else
                {
                    if (error is FaultException<ServiceFault>)
                    {
                        RaiseTaskExecutionProgress(TaskExecutionState.OperationError, error);

                        RaiseTaskExecutionProgress(TaskExecutionState.DisposingProxy, error);
                        m_Proxy.Close();
                    }
                    else
                    {
                        RaiseTaskExecutionProgress(TaskExecutionState.UnknownError, error);

                        m_Proxy.Abort();
                        RaiseTaskExecutionProgress(TaskExecutionState.ProxyDisposed, error);

                        m_Proxy = null;
                    }
                }

                RaiseTaskExecutionCompleted(false, error, null);
            }
        }
        /// <summary>
        /// Determina l'avvio della procedura che permette di inviare il task specificato al servizio di elaborazione,
        /// di verificarne periodicamente il completamento e di eseguire alla fine il download dei relativi risultati
        /// eventualmente prodotti.
        /// </summary>
        /// <remarks>
        /// Prima di invocare questo metodo, è necessario utilizzare il metodo SetTaskData per poter impostare i dati
        /// relativi al task da elaborare.
        /// Inoltre, questo metodo non dovrebbe essere invocato sul thread della UI, in quanto la verifica periodica
        /// del completamento dell'elaborazione blocca tale thread per l'intervallo di polling specificato.
        /// </remarks>
        public void Start()
        {
            if (m_Proxy == null)
            {
                try
                {
                    RaiseTaskExecutionProgress(TaskExecutionState.InitializingProxy, null);
                    m_Proxy = new ProcessingServiceClient(m_Binding, m_Endpoint);
                    m_Proxy.Open();
                    RaiseTaskExecutionProgress(TaskExecutionState.ProxyInitialized, null);

                    RaiseTaskExecutionProgress(TaskExecutionState.SendingRequest, null);
                    m_TaskRequestId = m_Proxy.SubmitData(m_TaskData);
                    RaiseTaskExecutionProgress(TaskExecutionState.RequestSent, null);

                    bool completed = false;
                    while (!completed)
                    {
                        m_PollingWaitHandle.WaitOne(m_PollingInterval);
                        TaskState ts = m_Proxy.GetState(m_TaskRequestId);

                        switch (ts)
                        {
                            case TaskState.Completed:
                                RaiseTaskExecutionProgress(TaskExecutionState.DownloadingResults, null);
                                m_TaskResults = m_Proxy.GetResults(m_TaskRequestId);
                                RaiseTaskExecutionProgress(TaskExecutionState.ResultsDownloaded, null);
                                completed = true;
                                break;

                            case TaskState.None:
                                completed = true;
                                break;

                            default:
                                completed = false;
                                break;
                        }
                    }

                    RaiseTaskExecutionProgress(TaskExecutionState.DisposingProxy, null);
                    m_Proxy.Close();
                    RaiseTaskExecutionProgress(TaskExecutionState.ProxyDisposed, null);

                    RaiseTaskExecutionCompleted(false, null, m_TaskResults);
                }
                catch (Exception ex)
                {
                    HandleError(ex);
                }
            }
        }
        /// <summary>
        /// Questo metodo viene automaticamente invocato nel momento in cui il proxy completa la chiusura del canale
        /// di comunicazione e permette di rimuovere la registrazione degli eventi associati al proxy e di riportare
        /// quest'ultimo al valore null.
        /// </summary>
        /// <param name="sender">l'oggetto che ha generato l'evento</param>
        /// <param name="args">informazioni aggiuntive sull'evento</param>
        private void Proxy_CloseCompleted(object sender, AsyncCompletedEventArgs args)
        {
            if (HandleCancellationIfRequired(args.Cancelled)) return;
            if (HandleErrorIfRequired(args.Error)) return;

            RaiseTaskExecutionProgress(TaskExecutionState.ProxyDisposed, null);

            UnregisterProxyEvents();
            m_Proxy = null;
        }
        /// <summary>
        /// Permette di gestire un errore verificatosi durante la procedura e restituisce true se è stata gestita,
        /// altrimenti false se non è stato necessario gestirlo poiché di fatto non è avvenuta alcun errore.
        /// </summary>
        /// <param name="error">l'eccezione che ha provocato l'errore</param>
        /// <returns>true se l'eventuale errore è stata gestito, altrimenti false</returns>
        private bool HandleErrorIfRequired(Exception error)
        {
            if (error != null)
            {
                TaskExecutionState state;
                if (m_CommunicationErrorsMapping.TryGetValue(error.GetType(), out state))
                {
                    RaiseTaskExecutionProgress(state, error);

                    m_Proxy.Abort();
                    RaiseTaskExecutionProgress(TaskExecutionState.ProxyDisposed, error);

                    UnregisterProxyEvents();
                    m_Proxy = null;
                }
                else
                {
                    if (error is FaultException<ServiceFault>)
                    {
                        RaiseTaskExecutionProgress(TaskExecutionState.OperationError, error);
                        BeginCloseProxy();
                    }
                    else
                    {
                        RaiseTaskExecutionProgress(TaskExecutionState.UnknownError, error);

                        m_Proxy.Abort();
                        RaiseTaskExecutionProgress(TaskExecutionState.ProxyDisposed, error);

                        UnregisterProxyEvents();
                        m_Proxy = null;
                    }
                }

                RaiseTaskExecutionCompleted(false, error, null);

                return true;
            }

            return false;
        }
        /// <summary>
        /// Inizia la configurazione del proxy per la comunicazione col servizio di elaborazione.
        /// </summary>
        private void BeginOpenProxy()
        {
            RaiseTaskExecutionProgress(TaskExecutionState.InitializingProxy, null);

            m_Proxy = new ProcessingServiceClient(m_Binding, m_Endpoint);

            m_Proxy.OpenCompleted += new EventHandler<AsyncCompletedEventArgs>(Proxy_OpenCompleted);
            m_Proxy.CloseCompleted += new EventHandler<AsyncCompletedEventArgs>(Proxy_CloseCompleted);
            m_Proxy.SubmitDataCompleted += new EventHandler<SubmitDataCompletedEventArgs>(Proxy_SubmitDataCompleted);
            m_Proxy.GetStateCompleted += new EventHandler<GetStateCompletedEventArgs>(Proxy_GetStateCompleted);
            m_Proxy.GetResultsCompleted += new EventHandler<GetResultsCompletedEventArgs>(Proxy_GetResultsCompleted);

            m_Proxy.OpenAsync();
        }
 /// <summary>
 /// Rilascia le risorse utilizzate per la comunicazione col server di elaborazione.
 /// </summary>
 public void Close()
 {
     lock (m_SyncLock)
     {
         if (m_InternalProxy != null)
         {
             try
             {
                 m_InternalProxy.Close();
             }
             catch
             {
                 if (m_InternalProxy.State == CommunicationState.Faulted)
                 {
                     m_InternalProxy.Abort();
                 }
             }
             finally
             {
                 m_InternalProxy = null;
                 m_MonitorClosed = true;
             }
         }
     }
 }
        /// <summary>
        /// Interroga il servizio di elaborazione remota specificato al momento della creazione di questo oggetto
        /// al fine di ricevere le eventuali risorse di calcolo messe a disposizione da tale servizio e l'istante
        /// di ricezione della risposta da parte del servizio stesso. Se non si verifica nessun errore durante la
        /// comunicazione col servizio, questo metodo restituisce true; in caso contratio restituisce false.
        /// </summary>
        /// <param name="resources">L'elenco risorse eventualmente disponibili sul server di elaborazione.</param>
        /// <param name="detection">L'istante di più recente rilevazione del server di elaborazione.</param>
        /// <returns>true se non si verifica nessun errore durante la comunicazione; in caso contrario false.</returns>
        public bool TryGetResources(out IEnumerable<TaskPerformerInfo> resources, out DateTime detection)
        {
            lock (m_SyncLock)
            {
                if (m_MonitorClosed || m_BindingConfig == null || m_RemoteUri == null)
                {
                    resources = default(IEnumerable<TaskPerformerInfo>);
                    detection = default(DateTime);
                    return false;
                }

                try
                {
                    if (m_InternalProxy == null)
                    {
                        m_InternalProxy = new ProcessingServiceClient(m_BindingConfig, new EndpointAddress(m_RemoteUri));
                        m_InternalProxy.Open();
                    }

                    string[] resourcesList = m_InternalProxy.QueryForEnabledResources();
                    detection = DateTime.Now;

                    HashSet<TaskPerformerInfo> resourceSet = new HashSet<TaskPerformerInfo>();
                    foreach (var resourceItem in resourcesList)
                        resourceSet.Add(new TaskPerformerInfo(resourceItem));

                    resources = resourceSet;
                    return true;
                }
                catch
                {
                    if (m_InternalProxy != null)
                    {
                        if (m_InternalProxy.State == CommunicationState.Faulted)
                        {
                            m_InternalProxy.Abort();
                            m_InternalProxy = null;
                        }
                    }

                    resources = default(IEnumerable<TaskPerformerInfo>);
                    detection = default(DateTime);
                    return false;
                }
            }
        }