/// <summary> /// Write configuration message and wait for the transmission to complete without caring about /// it being acknowledge or not. /// </summary> /// <param name="data">Communication message to be transmitted</param> /// <returns>Task represent the WriteConfigIgnoreResultAsync execution</returns> public async Task WriteConfigIgnoreResultAsync(UBXModelBase data) { if (!UBXModelBase.IsConfigMessage(data)) { throw new NotSupportedException("WriteConfig only available for config type UBX message"); } var notifyToken = new CancellationTokenSource(); // Register to wait for transmission this.transmissionNotification.Add(data, notifyToken); // Put in queue TransmitMessage(data); // Wait until the message is transmitted try { await Task.Delay(-1, notifyToken.Token); } catch (TaskCanceledException) { } // Remove from notification list this.transmissionNotification.Remove(data); }
/// <summary> /// Asynchronously request for message of type from the device. /// </summary> /// <typeparam name="T">Type of message that is requested.</typeparam> /// <returns> /// Task represents the PollMessageAsync execution, which upon completion returns the data /// requested from the device, or null if it is aborted by calling <see cref="AbortPollMessageAsync{T}"/>. /// </returns> public async Task <T> PollMessageAsync <T>() where T : UBXModelBase { var pollMessage = UBXModelBase.GetPollMessage <T>(); // Send the data TransmitMessage(pollMessage); // Wait until expected response comes return(await expectingList.ExpectAsync <T>()); }
/// <summary> /// Write configuration message to the device and wait until acknowledge or not-acknowledge message /// arrived /// </summary> /// <param name="data">Communication message to be transmitted </param> /// <returns> /// Task represents the WriteConfigAsync execution, which upon completion returns boolean where /// true means the configuration is acknowledged, and false otherwise. /// </returns> public async Task <bool> WriteConfigAsync(UBXModelBase data) { if (!UBXModelBase.IsConfigMessage(data)) { throw new NotSupportedException("WriteConfig only available for config type UBX message"); } TransmitMessage(data); var attr = UBXModelBase.GetMessageAttribute(data); return(await expectingList.ExpectAcknowledgeAsync(attr.ClassID, attr.MessageID)); }
public static bool IsConfigMessage(UBXModelBase message) { var attribute = message.GetType().GetTypeInfo().GetCustomAttribute <UBXConfigAttribute>(); if (attribute != null) { return(true); } else { return(false); } }
/// <summary> /// Create description key for expecting a message of type. /// </summary> /// <param name="message">The message</param> public ExpectingDescription(UBXModelBase message) { if (message is AcknowledgeBase) { var ack = message as AcknowledgeBase; this.classId = ack.ClassID; this.messageId = ack.MessageID; this.expectedMessageMode = ExpectingMode.Acknowledge; } else { this.expectingType = message.GetType(); this.expectedMessageMode = ExpectingMode.Regular; } }
/// <summary> /// Notify any party if waits a message of this type /// </summary> /// <param name="obj">The message to be notified</param> /// <returns>True if there is a party that is waiting for this kind of message, false otherwise.</returns> public bool NotifyIfExpected(UBXModelBase obj) { var expectingDesc = new ExpectingDescription(obj); try { var expectingContext = expectingList[expectingDesc]; expectingContext.NotifyResponseReceived(obj); return(true); } catch (KeyNotFoundException) { return(false); } }
internal static UBXMessageAttribute GetMessageAttribute(UBXModelBase message) { return(message.GetType().GetTypeInfo().GetCustomAttribute <UBXMessageAttribute>()); }
public static UBXModelBase TryParse(byte[] payload) { if (payload[0] != Header1 || payload[1] != Header2) { throw new InvalidMessageHeaderException(); } byte classId = payload[2]; byte messageId = payload[3]; int messageLength = payload[4] | (payload[5] << 8); try { var ubxType = parsableTypeIndex[new UBXMessageIndex(classId, messageId)]; ushort expectedChecksum = (ushort)((payload[payload.Length - 2]) | (payload[payload.Length - 1] << 8)); ushort computedChecksum = GetChecksum(payload, 2, payload.Length - 2); if (expectedChecksum != computedChecksum) { throw new InvalidChecksumException(String.Format("Checksum expected {0}, computed {1}", expectedChecksum, computedChecksum)); } BinaryReader reader = new BinaryReader(new MemoryStream(payload, 6, messageLength)); UBXModelBase retVal = (UBXModelBase)Activator.CreateInstance(ubxType.MessageClass); foreach (var property in ubxType.PropertyMap) { if (!property.ListType) { // If property is not a list type, parse normally using its underlying type property.Property.SetValue(retVal, reader.Read(property.Property.PropertyType)); } else { // If property is a list type, infer the type content var typeInfoOfPropertyType = property.Property.PropertyType.GetTypeInfo(); var theStructureType = typeInfoOfPropertyType.GenericTypeArguments[0]; // Get the T of IEnumerable<T> // Get the size of the structure var structureSize = UBXStructureMapper.PayloadSizeOf(theStructureType); // Get the item count var itemCount = Convert.ToInt32(ubxType.PropertyMap[property.ListIndexRef.Value].Property.GetValue(retVal)); // Construct list of it var theList = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(theStructureType)); for (int i = 0; i < itemCount; i++) { var buf = reader.ReadBytes(structureSize); theList.Add(UBXStructureMapper.TryParse(theStructureType, buf)); } // Set the value to property property.Property.SetValue(retVal, theList); } } return(retVal); } catch (KeyNotFoundException) { throw new UnknownMessageException(string.Format("Unknown message with Class: {0}, MessageID: {1}", classId, messageId)); } catch (NotSupportedException ex) { throw new UnknownMessageException(string.Format("Failed to parse Class: {0}, MessageID: {1}", classId, messageId), ex); } }
/// <summary> /// Instantiate MessageReceivedEventArgs. /// </summary> /// <param name="receivedMessage">The received message object.</param> internal MessageReceivedEventArgs(UBXModelBase receivedMessage) { this.ReceivedMessage = receivedMessage; }
/// <summary> /// Raise MessageReceived event with the specified message object. /// </summary> /// <param name="receivedMessage">Message object that is just received.</param> protected void OnMessageReceived(UBXModelBase receivedMessage) { this.MessageReceived?.Invoke(this, new MessageReceivedEventArgs(receivedMessage)); }
/// <summary> /// Transmit any message to the device /// </summary> /// <param name="messageToTransmit">Message to transmit to the device</param> public void TransmitMessage(UBXModelBase messageToTransmit) { transmitQueue.Enqueue(messageToTransmit); }
/// <summary> /// Communication loop for GPS serial device /// </summary> /// <param name="baudRate">Baud rate for this communication loop</param> /// <returns>Task that represent the asyncrhonous operation of communication loop</returns> private async Task Listen(uint baudRate) { // If it is already running, just bye if (running) { return; } // Initialize state and cancellation token to stop this operation running = true; cancelToken = new CancellationTokenSource(); DataReader dataReader = null; DataWriter dataWriter = null; SerialDevice serialPort = null; #if DEBUG Debug.WriteLine("Listening start..."); #endif try { // Get serial device from device info serialPort = await InitializeSerialPort(this.deviceInfo, baudRate); serialPort.WriteTimeout = TimeSpan.FromMilliseconds(100); serialPort.ReadTimeout = TimeSpan.FromMilliseconds(100); dataReader = new DataReader(serialPort.InputStream); dataReader.InputStreamOptions = InputStreamOptions.Partial; dataWriter = new DataWriter(serialPort.OutputStream); // Queue to store current byte processed, in case of data is transmitted // intersecting between two loop Queue <byte> currentlyProcessed = new Queue <byte>(1024); // Current communication state of the loop byte currentState = 0; ushort payloadLength = 0; while (true) { cancelToken.Token.ThrowIfCancellationRequested(); // Write any queued message await WriteQueuedMessages(dataWriter); // Longer timeout to prevent package drop CancellationTokenSource timeoutToken = new CancellationTokenSource(serialPort.ReadTimeout.Milliseconds * 10); // Load asynchronously, while at the same time we process the data that is ready var loadingTask = dataReader.LoadAsync(BufferLength).AsTask(timeoutToken.Token); while (dataReader.UnconsumedBufferLength != 0) { // Consume buffer while waiting for data byte currentByte = dataReader.ReadByte(); bool fail = false; currentlyProcessed.Enqueue(currentByte); // State machine: // 0: Header 1 // 1: Header 2 // 2: Class ID // 3: Message ID // 4: Least significant byte of size // 5: Most significant byte of size // 6: Payload with 1st byte of checksum // 7: 2nd byte of checksum // 8: Processing switch (currentState) { case 0: // Start with Header 1 if (currentByte != UBXModelBase.Header1) { fail = true; } break; case 1: // Followed by Header 2, otherwise fail if (currentByte != UBXModelBase.Header2) { fail = true; } break; case 4: // Retrieve Size payloadLength = currentByte; break; case 5: // Continue retrieve size payloadLength |= ((ushort)(currentByte << 8)); // Second byte of payload length break; } #if DEBUG && VERBOSE Debug.Write(currentByte.ToString("X") + ' '); #endif // Reset processing if it encounter invalid header if (fail) { currentState = 0; currentlyProcessed.Clear(); #if DEBUG && VERBOSE Debug.WriteLine(""); #endif } else if (currentState != 6) { // Increment state currentState++; } else if (currentState == 6) { // Loading the payload if (payloadLength > 0) { payloadLength--; } else { currentState++; } } // If the entire message has been received properly // perform processing if (currentState == 8) { try { var arr = currentlyProcessed.ToArray(); #if DEBUG Debug.WriteLine("Package received: " + currentlyProcessed.Count + " bytes"); #if VERBOSE DebugHelper.PrintArray(arr); #endif #endif // Parse the byte to UBX models var package = UBXModelBase.TryParse(arr); // Notify if any party is waiting this kind of message if (!expectingList.NotifyIfExpected(package)) { OnMessageReceived(package); } #if DEBUG Debug.WriteLine(package.ToString()); #endif } catch (UBXException ex) { #if DEBUG Debug.WriteLine("Failed parsing UBX package: " + ex); #endif } catch (Exception ex) { #if DEBUG Debug.WriteLine("Exception occured during parsing: " + ex, ex); #endif } finally { currentlyProcessed.Clear(); currentState = 0; } } } try { uint readedBuffer = await loadingTask; } catch (TaskCanceledException) { } catch (Exception ex) { #if DEBUG Debug.WriteLine("Unexpected Exception when awaiting loading: " + ex); #endif } } } catch (OperationCanceledException) { #if DEBUG Debug.WriteLine("Listening port stopped!"); #endif } catch (Exception ex) { #if DEBUG Debug.WriteLine("Unexpected Exception happen during the loop: " + ex); #endif } finally { if (dataReader != null) { dataReader.DetachStream(); dataReader.Dispose(); dataReader = null; } if (serialPort != null) { serialPort.Dispose(); serialPort = null; } running = false; cancelToken = null; } }
/// <summary> /// Notify the waiting party that the response is received /// </summary> /// <param name="obj">The message that is received</param> public void NotifyResponseReceived(UBXModelBase obj) { this.ResponseReceived = obj; notifyTokenSource.Cancel(); }