protected virtual void RaiseObisDataEvent(ObisData data)
 {
     // Raise the event in a thread-safe manner using the ?. operator.
     ObisDataEvent?.Invoke(this, new ObisDataEventArgs(data.Raw, data.Code, data.Value, data.Unit));
 }
        private async Task <bool> ReadObisMessageAsync()
        {
            var code     = new StringBuilder();
            var value    = new StringBuilder();
            var unit     = new StringBuilder();
            var raw      = new StringBuilder();
            var obisdata = new List <ObisData>();

            var state = ParseState.OBIS_BLOCK_START;

            while (!_token.IsCancellationRequested && (state != ParseState.STOPPARSING))
            {
                var data = await _reader.ReadByteAsync();

                _hexDump.DumpByte(HexDumpMode.BytesIn, data);

                switch (state)
                {
                case ParseState.OBIS_BLOCK_START:
                    if (data == Constants.FrameStart)
                    {
                        state = ParseState.OBIS_CODE;
                    }
                    break;

                case ParseState.OBIS_CODE:
                    if (data == Constants.EndMessage)
                    {
                        state = ParseState.OBIS_BLOCK_END;
                        break;
                    }
                    if ((char)data == '(')
                    {
                        raw.Append((char)data);
                        state = ParseState.OBIS_VALUE;
                        break;
                    }
                    if (code.Length < Constants.ObisCodeMaxLength)
                    {
                        raw.Append((char)data);
                        code.Append((char)data);
                    }
                    else
                    {
                        throw new Exception();
                    }
                    break;

                case ParseState.OBIS_VALUE:
                    if ((char)data == '*')
                    {
                        raw.Append((char)data);
                        state = ParseState.OBIS_UNIT;
                        break;
                    }
                    if ((char)data == ')')
                    {
                        raw.Append((char)data);
                        state = ParseState.OBIS_ITEM_END;
                        break;
                    }
                    if (value.Length < Constants.ObisValueMaxLength)
                    {
                        raw.Append((char)data);
                        value.Append((char)data);
                    }
                    else
                    {
                        throw new Exception();
                    }
                    break;

                case ParseState.OBIS_UNIT:
                    if ((char)data == ')')
                    {
                        raw.Append((char)data);
                        state = ParseState.OBIS_ITEM_END;
                        break;
                    }
                    if (unit.Length < Constants.ObisUnitMaxLength)
                    {
                        raw.Append((char)data);
                        unit.Append((char)data);
                    }
                    else
                    {
                        throw new Exception();
                    }
                    break;

                case ParseState.OBIS_ITEM_END:
                    if (data == Constants.CR)
                    {
                        continue;
                    }
                    if (data == Constants.LF)
                    {
                        var item = new ObisData(raw.ToString(), code.ToString(), value.ToString(), unit.ToString());
                        obisdata.Add(item);
                        RaiseObisDataEvent(item);
                        raw.Clear();
                        code.Clear();
                        value.Clear();
                        unit.Clear();
                        state = ParseState.OBIS_CODE;
                        break;
                    }
                    break;

                case ParseState.OBIS_BLOCK_END:
                    if (data == Constants.CR || data == Constants.LF)
                    {
                        continue;
                    }
                    if (data == Constants.FrameEnd)
                    {
                        state = ParseState.STOPPARSING;
                        break;
                    }
                    throw new Exception();     // unexpected character

                default:
                    break;
                }
            }
            return(true);
        }