private void DevLink_Received_CallBack(uint pbxh, StringBuilder rawDevLink) // CallEvent { // TODO: TRY - CATCH DEBUGLogger.WriteThreadInfo("DevLink_Received_CallBack"); DEBUGLogger.WriteLine("pbxh=" + pbxh + "; info=" + rawDevLink); _rawDevLinkQueue.Enqueue(new DevLinkMessage() { IpAddress = IpAddress, RawDevLink = rawDevLink.ToString(), SiteId = _iPOID }); // Make thread-safe: since this method is a CallBack, it will be called by Avaya's DevLink code // in its's thread, which will be different from any of the possible subscriber's threads. // http://broadcast.oreilly.com/2010/09/understanding-c-raising-events.html var devLinkRecieved_threadSafe = DevLinkRecieved; // Multi-cast delegates are immutable, so whatever happens to DevLinkRecieved from here on will NOT affect devLinkRecieved_threadSafe if (devLinkRecieved_threadSafe != null) { devLinkRecieved_threadSafe(this, new DevLinkEventArgs((int)pbxh, rawDevLink.ToString())); // NOTE: // When an event fires, all of the registered handlers execute on the same thread (the one that raised the event - // in our case - the thread belongs to Avaya's DevLink!). // There's no built-in facility for events to fire on multiple threads. // http://stackoverflow.com/questions/3480036/multiple-threads-subscribing-same-event } }
private void CloseConnection() { // We may want to call this just in case (when connection timeout expires) so we don't get any more calls back from DevLink! if (ConnectionState != ConnectionStates.Disconnected) { // Disconnect from an IP Office system: // This routine may return 0 (DEVLINK_SUCCESS) or 1 (DEVLINK_UNSPECIFIEDFAIL) in the event of an error. // [AK:] OR a big number >> 1, in the event it is called twice for the same IPO. // TODO: should we check for ret > 0 ??? long iRet = DLClose((uint)_iPOID); ConnectionState = ConnectionStates.Disconnected; // TODO: set the callback delegates to NULL??? // OR Just do NOTHING in the methods, if we are disconnected??? _disposed = true; DEBUGLogger.WriteLine("DLClose: iPOID = " + _iPOID + "; iRet = " + iRet); } }
private void Process() { DEBUGLogger.WriteThreadInfo("Process"); _handleDelta2Event = DevLink_Received_CallBack; /*This routine may return:- * 0 = DEVLINK_SUCCESS * 1 = DEVLINK_UNSPECIFIEDFAIL - Returned in the event of an error. * 2 = DEVLINK_LICENCENOTFOUND - If no CTI license is activated on the IP Office system. */ long iRet = DLRegisterType2CallDeltas((uint)_iPOID, _handleDelta2Event // DLCALLLOGEVENT /* (pbxh, rawDevLink) => { lock (thisLock) * if (default(EventHandler<DevLinkEventArgs>) != DevLinkRecieved) * { * DevLinkRecieved(this, new DevLinkEventArgs(pbxh, rawDevLink)); * } } */ ); DEBUGLogger.WriteLine("DLRegisterType2CallDeltas return value = " + iRet); }
/// <summary> /// DLOpen callback /// </summary> /// <param name="pbxh"></param> /// <param name="comms_state"></param> /// <param name="parm1"></param> private void DevLink_Connection_CallBack(uint pbxh, int comms_state, int parm1) // oCommsEvent CommEvent { DEBUGLogger.WriteThreadInfo("DevLink_Connection_CallBack"); // TODO: TRY - CATCH DEBUGLogger.WriteLine("DevLink_Connection_CallBack: iPOID = " + pbxh + "; comms_state = " + comms_state + "; parm1 = " + parm1); if (_ignoreConnectionCallback) { return; // Whatever! } lock (_connectionLock) { if (_ignoreConnectionCallback) { // It is too late, we have already given up on this method returning! DEBUGLogger.WriteLine("DevLink_Connection_CallBack: Too late, the connection callback result (comms_state=" + comms_state + "; parm1=" + parm1 + ") is being ignored."); return; } switch ((ConnectionResponse)comms_state) { // Communications established. This occurs either after the initial call to DLOpen(), // or after the system unit has come back on-line after being powered off or rebooted. // TODO: does the above mean that this method will be called again if the system is rebooted...?? Think of how to handle this case! case ConnectionResponse.DEVLINK_COMMS_OPERATIONAL_0: { ConnectionState = ConnectionStates.Connected; // OK, we're done here, notify the main thread: // TODO: what if this happens before the WaitOne call? OK I guess??? _waitConnectionOrTimeoutHandle.Set(); return; } // No response from system unit. This occurs either (1) after the initial call to DLOpen(), or // (2) if the system unit is powered off/rebooted, or (3) if network problems prevent communications. case ConnectionResponse.DEVLINK_COMMS_NORESPONSE_1: { // TODO: LOG a message, and wait more for possible recovery. ConnectionState = ConnectionStates.Failed; return; } // Reserved for future use OR incorrect system password specified (????) case ConnectionResponse.DEVLINK_COMMS_REJECTED_2: { _connectionCallbackMessage = "Received response DEVLINK_COMMS_REJECTED - Reserved for future use! Do we have to implement this case too?"; break; } // Packets were generated by IPO, but were not received by DevLink. // This can occur either because the IPO is under heavy load (IPPO always prioritizes data routing and call handling above CTI events // - parm1 contains the number of packets missed) // or because the application using DevLink did not return from a callback quickly enough. // Applications should ensure that they do not take more than 100 milliseconds to process events. case ConnectionResponse.DEVLINK_COMMS_MISSEDPACKETS_3: { _connectionCallbackMessage = "There are " + parm1 + " lost packets! IPO may be under heavy load. Ensure that it takes no more than 100 ms to process events!"; break; } default: _connectionCallbackMessage = "Ooops, got unexpected result! comms_state = " + comms_state; break; } // Hmm, we were not able to connect successfully. We WON'T call _waitConnectionOrTimeoutHandle.Set() now, to give it some more time/chance to connect till the timeout. ConnectionState = ConnectionStates.Failed; } }
private bool StartMonitoring() { // Start the connecting process. Connecting will be set to FALSE once IsConnected is assigned a value. ConnectionState = ConnectionStates.Connecting; _ignoreConnectionCallback = false; DEBUGLogger.WriteLine("Waiting for connection response..."); // Store a reference to the delegate instance in a private class field before passing it to the native code - // to prevent the garbage collector from collecting it! _handleCommsEvent = DevLink_Connection_CallBack; // Create the wait-handle. This thread will wait till the callback returns, or the timeout expires. _waitConnectionOrTimeoutHandle = new AutoResetEvent(false); // Call the DevLink method to connect: long iRet = DLOpen((uint)_iPOID, IpAddress, _password, null, null, _handleCommsEvent); // TODO: does it make sense to check for the return value here??? DEBUGLogger.WriteLine("DLOpen: iPOID = " + _iPOID + "; iRet = " + iRet); // There is a sliiight possibility that HandleCommsEvent is called and returns before we get to the "...WaitOne()" call below. // But we're not going to bother with this - if so, WaitOne will just wait till the timeout. // (If we decide to check the ConnectionState here, before calling WaitOne, nothing will prevent it from changing // juuust after our check and before the WaitOne call (unless we lock()).) // Now wait till the Connection-Callback returns, or the timeout expires: _waitConnectionOrTimeoutHandle.WaitOne(_connectionTimeoutSec); // this should return 'false' in case of timeout, but we don't need the result // OK, the event was signaled - let's see what this means for the Connection State... lock (_connectionLock) { if (_connectionCallbackMessage.Length > 0) { DEBUGLogger.WriteLine(_connectionCallbackMessage); } switch (ConnectionState) { case ConnectionStates.Connected: DEBUGLogger.WriteLine("Connection within timeout!..."); break; case ConnectionStates.Failed: DEBUGLogger.WriteLine("Connection problems! ConnectionState: " + ConnectionState.ToString()); break; case ConnectionStates.Connecting: ConnectionState = ConnectionStates.TimedOut; DEBUGLogger.WriteLine("Connection Timeout expired!..."); break; case ConnectionStates.Disconnected: DEBUGLogger.WriteLine("Unexpected connection state in StartMonitoring() - Disconnected"); break; } // We got a response OR the waiting timed out. Either way, we're done with the CallBack, so let it know: _ignoreConnectionCallback = true; } // un-locking if (ConnectionState == ConnectionStates.Connected) { // Start the DevLink processing: // TODO: Maybe move this to the lock..??? Process(); } else { // There was a connection error, or we gave up waiting // (but DevLink does not know it, and could still call the callback if the timeout interval turned out to be too small) // We don't want surprises, so we'd better close the connection, just in case: CloseConnection(); } // We should not need this anymore, so close it: _waitConnectionOrTimeoutHandle.Close(); return(ConnectionState == ConnectionStates.Connected); }