/// <summary> /// Decoder step 1: get message header 10 bytes /// </summary> /// <param name="length"></param> /// <param name="need"></param> /// <returns>The decoder step to execute next.</returns> private int DecoderStep1GetMessageHeader(ref int length, out int need) { if (!StreamDecoder.CheckAvailable(length, 10, out need)) { return(1); } this.messageHeader = MessageHeader.Decode(new ReadOnlySpan <byte>(this.Buffer, this.decodeIndex, 10)); this.decodeIndex += 10; this.messageDataLength -= 10; length -= 10; if (this.messageDataLength == 0) { if (this.messageHeader.MessageType == MessageType.DataMessage) { this.dataMessageHandler(this.messageHeader, new SecsMessage(this.messageHeader.S, this.messageHeader.F, string.Empty, replyExpected: this.messageHeader.ReplyExpected)); } else { this.controlMessageHandler(this.messageHeader); } return(0); } if (length >= this.messageDataLength) { Trace.WriteLine("Get Complete Data Message with total data"); this.dataMessageHandler(this.messageHeader, new SecsMessage(this.messageHeader.S, this.messageHeader.F, string.Empty, StreamDecoder.BufferedDecodeItem(this.Buffer, ref this.decodeIndex), this.messageHeader.ReplyExpected)); length -= (int)this.messageDataLength; this.messageDataLength = 0; return(0); //completeWith message received } return(this.DecoderStep2GetItemHeader(ref length, out need)); }
private static Item BufferedDecodeItem(byte[] bytes, ref int index) { byte formatCodeValue = bytes[index]; var format = (SecsFormat)(formatCodeValue & 0b_111111_00); byte lengthBits = (byte)(formatCodeValue & 0b_000000_11); index++; byte[] itemLengthBytes = new byte[4]; Array.Copy(bytes, index, itemLengthBytes, 0, lengthBits); Array.Reverse(itemLengthBytes, 0, lengthBits); int dataLength = BitConverter.ToInt32(itemLengthBytes, 0); // max to 3 byte dataLength index += lengthBits; if (format == SecsFormat.List) { if (dataLength == 0) { return(Item.L()); } var list = new List <Item>(dataLength); for (int i = 0; i < dataLength; i++) { list.Add(StreamDecoder.BufferedDecodeItem(bytes, ref index)); } return(Item.L(list)); } var item = Item.BytesDecode(format, bytes, index, dataLength); index += dataLength; return(item); }
/// <summary> /// Decoder step 2: get _format + lengthBits(2bit) 1 byte /// </summary> /// <param name="length"></param> /// <param name="need"></param> /// <returns>The decoder step to execute next.</returns> private int DecoderStep2GetItemHeader(ref int length, out int need) { if (!StreamDecoder.CheckAvailable(length, 1, out need)) { return(2); } this.format = (SecsFormat)(this.Buffer[this.decodeIndex] & 0xFC); this.lengthBits = (byte)(this.Buffer[this.decodeIndex] & 3); this.decodeIndex++; this.messageDataLength--; length--; return(this.DecoderStep3GetItemLength(ref length, out need)); }
/// <summary> /// Decoder step 0: get total message length 4 bytes /// </summary> /// <param name="length"></param> /// <param name="need"></param> /// <returns>The decoder step to execute next.</returns> private int DecoderStep0GetTotalMessageLength(ref int length, out int need) { if (!StreamDecoder.CheckAvailable(length, 4, out need)) { return(0); } Array.Reverse(this.Buffer, this.decodeIndex, 4); this.messageDataLength = BitConverter.ToUInt32(this.Buffer, this.decodeIndex); Trace.WriteLine($"Get Message Length: {this.messageDataLength}"); this.decodeIndex += 4; length -= 4; return(this.DecoderStep1GetMessageHeader(ref length, out need)); }
/// <summary> /// Decoder step 3: get _itemLength _lengthBits bytes, at most 3 byte /// </summary> /// <param name="length"></param> /// <param name="need"></param> /// <returns>The decoder step to execute next.</returns> private int DecoderStep3GetItemLength(ref int length, out int need) { if (!StreamDecoder.CheckAvailable(length, this.lengthBits, out need)) { return(3); } Array.Copy(this.Buffer, this.decodeIndex, this.itemLengthBytes, 0, this.lengthBits); Array.Reverse(this.itemLengthBytes, 0, this.lengthBits); this.itemLength = BitConverter.ToInt32(this.itemLengthBytes, 0); Array.Clear(this.itemLengthBytes, 0, 4); Trace.WriteLineIf(this.format != SecsFormat.List, $"Get format: {this.format}, length: {this.itemLength}"); this.decodeIndex += this.lengthBits; this.messageDataLength -= this.lengthBits; length -= this.lengthBits; return(this.DecoderStep4GetItem(ref length, out need)); }
/// <summary> /// Decoder step 4: get item value /// </summary> /// <param name="length"></param> /// <param name="need"></param> /// <returns>The decoder step to execute next.</returns> private int DecoderStep4GetItem(ref int length, out int need) { need = 0; Item item; if (this.format == SecsFormat.List) { if (this.itemLength == 0) { item = Item.L(); } else { this.stack.Push(new List <Item>(this.itemLength)); return(this.DecoderStep2GetItemHeader(ref length, out need)); } } else { if (!StreamDecoder.CheckAvailable(length, this.itemLength, out need)) { return(4); } item = Item.BytesDecode(this.format, this.Buffer, this.decodeIndex, this.itemLength); Trace.WriteLine($"Complete Item: {this.format}"); this.decodeIndex += this.itemLength; this.messageDataLength -= (uint)this.itemLength; length -= this.itemLength; } if (this.stack.Count == 0) { Trace.WriteLine("Get Complete Data Message by stream decoded"); this.dataMessageHandler(this.messageHeader, new SecsMessage(this.messageHeader.S, this.messageHeader.F, string.Empty, item, this.messageHeader.ReplyExpected)); return(0); } var list = this.stack.Peek(); list.Add(item); while (list.Count == list.Capacity) { item = Item.L(this.stack.Pop()); Trace.WriteLine($"Complete List: {item.Count}"); if (this.stack.Count > 0) { list = this.stack.Peek(); list.Add(item); } else { Trace.WriteLine("Get Complete Data Message by stream decoded"); this.dataMessageHandler(this.messageHeader, new SecsMessage(this.messageHeader.S, this.messageHeader.F, string.Empty, item, this.messageHeader.ReplyExpected)); return(0); } } return(this.DecoderStep2GetItemHeader(ref length, out need)); }
/// <summary> /// constructor /// </summary> /// <param name="isActive">passive or active mode</param> /// <param name="ip">if active mode it should be remote device address, otherwise local listener address</param> /// <param name="port">if active mode it should be remote device listener's port</param> /// <param name="receiveBufferSize">Socket receive buffer size</param> public SecsGem(bool isActive, IPAddress ip, int port, int receiveBufferSize = 0x4000) { if (port <= 0) { throw new ArgumentOutOfRangeException(nameof(port), port, $"port number must greater than 0"); } _taskFactory = new TaskFactory(TaskScheduler.Default); _ip = ip; _port = port; _isActive = isActive; _secsDecoder = new StreamDecoder(receiveBufferSize, HandleControlMessage, HandleDataMessage); _decoderBufferSize = receiveBufferSize; #region Timer Action _timer7 = new Timer(delegate { _logger.Error($"T7 Timeout: {T7 / 1000} sec."); CommunicationStateChanging(ConnectionState.Retry); }, null, Timeout.Infinite, Timeout.Infinite); _timer8 = new Timer(delegate { _logger.Error($"T8 Timeout: {T8 / 1000} sec."); CommunicationStateChanging(ConnectionState.Retry); }, null, Timeout.Infinite, Timeout.Infinite); _timerLinkTest = new Timer(delegate { if (State == ConnectionState.Selected) { SendControlMessage(MessageType.LinkTestRequest, NewSystemId); } }, null, Timeout.Infinite, Timeout.Infinite); #endregion var receiveCompleteEvent = new SocketAsyncEventArgs(); receiveCompleteEvent.SetBuffer(_secsDecoder.Buffer, _secsDecoder.BufferOffset, _secsDecoder.BufferCount); receiveCompleteEvent.Completed += ReceiveEventCompleted; if (_isActive) { _startImpl = async() => { bool connected = false; do { if (IsDisposed) { return; } CommunicationStateChanging(ConnectionState.Connecting); try { if (IsDisposed) { return; } _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); await _socket.ConnectAsync(_ip, _port).ConfigureAwait(false); connected = true; } catch (Exception ex) { if (IsDisposed) { return; } _logger.Error(ex.Message); _logger.Info($"Start T5 Timer: {T5 / 1000} sec."); await Task.Delay(T5); } } while (!connected); CommunicationStateChanging(ConnectionState.Connected); // hook receive event first, because no message will received before 'SelectRequest' send to device if (!_socket.ReceiveAsync(receiveCompleteEvent)) { ReceiveEventCompleted(_socket, receiveCompleteEvent); } SendControlMessage(MessageType.SelectRequest, NewSystemId); }; //_stopImpl = delegate { }; } else { var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); server.Bind(new IPEndPoint(_ip, _port)); server.Listen(0); _startImpl = async() => { bool connected = false; do { if (IsDisposed) { return; } CommunicationStateChanging(ConnectionState.Connecting); try { if (IsDisposed) { return; } _socket = await server.AcceptAsync().ConfigureAwait(false); connected = true; } catch (Exception ex) { if (IsDisposed) { return; } _logger.Error(ex.Message); await Task.Delay(2000); } } while (!connected); CommunicationStateChanging(ConnectionState.Connected); if (!_socket.ReceiveAsync(receiveCompleteEvent)) { ReceiveEventCompleted(_socket, receiveCompleteEvent); } }; _stopImpl = delegate { if (IsDisposed) { server.Dispose(); } }; } _sendControlMessageCompleteHandler = SendControlMessageCompleteHandler; _sendDataMessageCompleteHandler = SendDataMessageCompleteHandler; }
/// <summary> /// <para xml:lang="en">Initializes a <see langword="new"/> instance of the <see cref="SecsGem"/> <see langword="class"/>.</para> /// </summary> /// <param name="isActive"><see langword="true"/> for active and <see langword="false"/> for passive mode.</param> /// <param name="ip">If active mode, it should be remote device address, otherwise local listener address.</param> /// <param name="port">If active mode, it should be remote device listener's port.</param> /// <param name="initialReceiveBufferSize">The initial socket receive buffer size.</param> public SecsGem(bool isActive, IPAddress ip, ushort port, int initialReceiveBufferSize = SecsGem.defaultInitalReceiveBufferSize) { if (initialReceiveBufferSize < 64) { throw new ArgumentOutOfRangeException(nameof(initialReceiveBufferSize), initialReceiveBufferSize, $"The buffet size is less than {64}. Recommended is the default value of {SecsGem.defaultInitalReceiveBufferSize}."); } this.secsDecoder = new StreamDecoder(initialReceiveBufferSize, this.HandleControlMessage, this.HandleDataMessage); this.IpAddress = ip; this.Port = port; this.IsActive = isActive; this.DecoderBufferSize = initialReceiveBufferSize; #region Timer Action this.timer7 = new Timer(delegate { this.logger.Error($"T7 Timeout: {this.T7 / 1000} sec."); this.CommunicationStateChanging(ConnectionState.Retry); }, null, Timeout.Infinite, Timeout.Infinite); this.timer8 = new Timer(delegate { this.logger.Error($"T8 Timeout: {this.T8 / 1000} sec."); this.CommunicationStateChanging(ConnectionState.Retry); }, null, Timeout.Infinite, Timeout.Infinite); this.timerLinkTest = new Timer(delegate { if (this.State == ConnectionState.Selected) { this.SendControlMessage(MessageType.LinkTestRequest, this.GetNewSystemId()); } }, null, Timeout.Infinite, Timeout.Infinite); #endregion if (this.IsActive) { this.startImplementationFunction = async() => { var connected = false; do { if (this.IsDisposed) { return; } this.CommunicationStateChanging(ConnectionState.Connecting); try { if (this.IsDisposed) { return; } this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); await this.socket.ConnectAsync(this.IpAddress, this.Port).ConfigureAwait(false); connected = true; } catch (Exception ex) { if (this.IsDisposed) { return; } this.logger.Error(ex.Message, ex); this.logger.Info($"Start T5 Timer: {this.T5 / 1000} sec."); await Task.Delay(this.T5).ConfigureAwait(false); } } while (!connected); // hook receive event first, because no message will received before 'SelectRequest' send to device this.StartSocketReceive(); this.SendControlMessage(MessageType.SelectRequest, this.GetNewSystemId()); }; } else { this.serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this.serverSocket.Bind(new IPEndPoint(this.IpAddress, this.Port)); this.serverSocket.Listen(0); this.startImplementationFunction = async() => { var connected = false; do { if (this.IsDisposed) { return; } this.CommunicationStateChanging(ConnectionState.Connecting); try { if (this.IsDisposed) { return; } this.socket = await this.serverSocket.AcceptAsync().ConfigureAwait(false); connected = true; } catch (Exception ex) { if (this.IsDisposed) { return; } this.logger.Error(ex.Message, ex); await Task.Delay(2000).ConfigureAwait(false); } } while (!connected); this.StartSocketReceive(); }; } }