private Task <ViscaPacket> SendAsync(CancellationToken cancellationToken, params byte[] packet) { var effectiveToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(CommandTimeout).Token).Token; var request = ViscaPacket.FromBytes(packet, 0, packet.Length); return(client.SendAsync(request, effectiveToken)); }
internal async Task <ViscaPacket> ReadAsync(Stream stream, CancellationToken cancellationToken) { while (true) { if (FindEnd() is int count) { return(Consume(count)); } if (size == buffer.Length) { // TODO: Should our buffer be smaller? throw new ViscaProtocolException($"Read {size} bytes without a reaching the end of a VISCA packet"); } using (var registration = cancellationToken.Register(() => stream.Close())) { int bytesRead = await stream.ReadAsync(buffer, size, buffer.Length - size).ConfigureAwait(false); if (bytesRead == 0) { throw new ViscaProtocolException("Reached end of VISCA stream"); } size += bytesRead; } } int?FindEnd() { for (int i = 0; i < size; i++) { if (buffer[i] == 0xff) { // Include the trailing byte return(i + 1); } } return(null); } ViscaPacket Consume(int count) { // Exclude the trailing byte from the packet var packet = ViscaPacket.FromBytes(buffer, 0, count - 1); size -= count; // Shift the content of the array if we need to. // (Alternatively, we could construct the packet from middle of the byte array, // and only shift when we got near the end of the buffer. It really shouldn't matter much though.) if (size != 0) { Buffer.BlockCopy(buffer, count, buffer, 0, size); } return(packet); } }
async Task <ViscaPacket> IViscaClient.SendAsync(ViscaPacket request, CancellationToken cancellationToken) { bool reconnect = true; await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { await SendPacketAsync(request, cancellationToken).ConfigureAwait(false); while (true) { ViscaPacket response = await ReceivePacketAsync(cancellationToken).ConfigureAwait(false); if (response.Length < 2) { throw new ViscaProtocolException($"Received packet of length {response.Length} from VISCA endpoint"); } switch (response.GetByte(1) >> 4) { // Command received case 4: continue; // Success case 5: reconnect = false; return(response); // Error reported by device case 6: throw new ViscaResponseException($"Error reported by VISCA interface. Error data: {response}"); // VISCA protocol violation default: throw new ViscaProtocolException($"Invalid packet returned from VISCA interface. Error data: {response}"); } } } finally { try { if (reconnect) { await ReconnectAsync(cancellationToken).ConfigureAwait(false); } } finally { semaphore.Release(); } } }
async Task <ViscaPacket> IViscaClient.SendAsync(ViscaPacket request, CancellationToken cancellationToken) { await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { if (request.GetByte(0) != 0x81 || request.Length < 5) { return(SyntaxError); } return(request.GetByte(1) switch { 0x01 => await ExecuteCommand(request).ConfigureAwait(false), 0x09 => ExecuteQuery(request), _ => SyntaxError }); }
/// <summary> /// Sends a single packet to the VISCA endpoint. /// </summary> /// <param name="packet">The packet to send to the endpoint.</param> /// <param name="cancellationToken">A token to indicate that the method has been cancelled.</param> /// <returns>A task representing the asynchronous operation.</returns> protected abstract Task SendPacketAsync(ViscaPacket packet, CancellationToken cancellationToken);