// Using a template, with a constraint of IMessage allows us to pass the message without boxing reducing garbage generation
        private unsafe void ReadMessage <T>(DateTime now, Command command) where T : unmanaged, IMessage
        {
            int length = 0;

            try
            {
                while (length < sizeof(T))
                {
                    length += m_SerialPort.Read(m_ReadBuffer, length, sizeof(T) - length);
                }

                if (length != sizeof(T))
                {
                    throw new ArgumentException($"Message Length: {length}. Expected Length: {sizeof(T)}.");
                }
            }
            catch (Exception e)
            {
                AppLogging.DebugLogException(nameof(ReadMessage), e);
                Interlocked.Increment(ref m_ErrorCount);
                return;
            }

            T message = new T();

            message.SetBytes(m_ReadBuffer);
            AppLogging.DebugLog(nameof(ReadMessage), command.ToString(), message.ToString());

            Interlocked.Add(ref m_ReadBytes, length);
            m_LastMessageRead = now;
            m_MessageContext.Post(x => OnMessageRecieved?.Invoke(command, message), null);
        }
Пример #2
0
        private void Connect(DateTime now)
        {
            string[] portNames = null;
            try { portNames = SerialPort.GetPortNames(); }
            catch { }

            if (portNames == null || portNames.Length == 0)
            {
                return;
            }

            foreach (string portName in portNames)
            {
                string firmware = "";
                try
                {
                    AppLogging.DebugLog(nameof(Connect), portName);
                    m_SerialPort              = new SerialPort(portName, 115200);
                    m_SerialPort.ReadTimeout  = k_ReadTimeout;
                    m_SerialPort.WriteTimeout = k_WriteTimeout;
                    m_SerialPort.Open();
                    m_SerialPort.DiscardInBuffer();
                    m_SerialPort.DiscardOutBuffer();

                    WriteMessage(now, Command.TEST);
                    Thread.Sleep(20);
                    Command command = (Command)m_SerialPort.ReadByte();
                    if (command != Command.TEST)
                    {
                        throw new InvalidOperationException($"Firmware Test reply failed. Reply: '{command}' Bytes: '{m_SerialPort.BytesToRead}'");
                    }
                    firmware = m_SerialPort.ReadLine().Replace("\r", "");
                    if (!FirmwareVersions.IsCompatible(firmware))
                    {
                        throw new ArgumentException($"Incompatible Firmware: '{firmware}'.");
                    }
                    m_SerialPort.DataReceived += Read;
                    m_DeviceConnected          = true;
                    m_DeviceReady              = true;
                    m_MessageContext.Post(x => OnDeviceConnected?.Invoke(), null);
                    m_LastMessageRead = now;
                    return;
                }
                catch (Exception e)
                {
                    AppLogging.DebugLogException(nameof(Connect), e);
                    m_SerialPort.Close();
                    m_SerialPort.Dispose();
                    m_SerialPort = null;

                    if (e is ArgumentException) // Incompatible Firmware
                    {
                        m_MessageContext.Post(x => OnFirmwareIncompatible?.Invoke(firmware), null);
                    }
                }
            }
        }
        private void WriteMessage(DateTime now, Command command, IMessage message = null)
        {
            m_WriteBuffer.SetLength(0);
            m_WriteBuffer.WriteByte((byte)command);
            message?.GetBytes(m_WriteBuffer);
            Interlocked.Add(ref m_WriteBytes, m_WriteBuffer.Length);

            // GetBuffer returns a reference to the underlying array, we can still use that after we reset the position if we store the length
            byte[] buffer = m_WriteBuffer.GetBuffer();
            int    length = (int)m_WriteBuffer.Length;

            AppLogging.DebugLog(nameof(WriteMessage), command.ToString(), message != null ? message.ToString() : "");

            try { m_SerialPort.Write(buffer, 0, length); }
            catch (Exception e)
            {
                AppLogging.DebugLogException(nameof(WriteMessage), e);
                return;
            }
            m_LastMessageWrite = now;
        }
        private void Read(DateTime now)
        {
#if POLLING_SERIAL
            try
            {
                if (m_SerialPort == null || !m_SerialPort.IsOpen || m_SerialPort.BytesToRead <= 0)
                {
                    return;
                }
            }
            catch { return; }
#endif

            Command command;
            try { command = (Command)m_SerialPort.ReadByte(); }
            catch (Exception ex)
            {
                AppLogging.DebugLogException(nameof(Read), ex);
                Interlocked.Increment(ref m_ErrorCount);
                return;
            }

            Interlocked.Increment(ref m_ReadCount);
            Interlocked.Increment(ref m_ReadBytes);
            switch (command)
            {
            case Command.TEST:
            {
                try
                {
                    var firmware = m_SerialPort.ReadLine().Replace("\r", "");
                    AppLogging.DebugLog(nameof(Read), command.ToString(), firmware);
                    m_LastMessageRead  = now;
                    m_LastMessageWrite = now;
                }
                catch (Exception e)
                {
                    AppLogging.DebugLogException(nameof(ReadMessage), e);
                    Interlocked.Increment(ref m_ErrorCount);
                    return;
                }
            }
            break;

            case Command.OK:
            {
                AppLogging.DebugLog(nameof(Read), command.ToString());
                m_DeviceReady      = true;
                m_LastMessageRead  = now;
                m_LastMessageWrite = now;
                Write(m_LastMessageRead);
            }
            break;

            case Command.SETTINGS:
                ReadMessage <DeviceSettings>(now, command);
                break;

            case Command.SESSION_INFO:
                ReadMessage <SessionInfo>(now, command);
                break;

            case Command.CURRENT_SESSION:
            case Command.ALTERNATE_SESSION:
            case Command.PREVIOUS_SESSION:
            case Command.NEXT_SESSION:
                ReadMessage <SessionData>(now, command);
                break;

            case Command.VOLUME_CURR_CHANGE:
            case Command.VOLUME_ALT_CHANGE:
            case Command.VOLUME_PREV_CHANGE:
            case Command.VOLUME_NEXT_CHANGE:
                ReadMessage <VolumeData>(now, command);
                break;

            case Command.MODE_STATES:
                ReadMessage <ModeStates>(now, command);
                break;

            case Command.ERROR:
            case Command.NONE:
            case Command.DEBUG:
                Interlocked.Increment(ref m_ErrorCount);
                break;
            }
        }