Skip to content

Commit

Permalink
Much faster updating (#390)
Browse files Browse the repository at this point in the history
  • Loading branch information
DTTerastar authored Nov 11, 2023
1 parent 0a04f57 commit 332ead3
Showing 1 changed file with 93 additions and 74 deletions.
167 changes: 93 additions & 74 deletions Network/ESPOta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,7 @@ public async Task<bool> Update(string remoteIp, int remotePort, EspOtaCommand co
{
_fs.Seek(0, SeekOrigin.Begin);

var listener = new TcpListener(IPAddress.Any, _localPort ?? 0)
{
Server =
{
SendTimeout = 10000,
ReceiveTimeout = 10000,
}
};

var listener = new TcpListener(IPAddress.Any, _localPort ?? 0);
try
{
listener.Start();
Expand All @@ -52,27 +44,22 @@ public async Task<bool> Update(string remoteIp, int remotePort, EspOtaCommand co
await _progress($"Server started. Listening to TCP clients at 0.0.0.0:{_localPort}", 0);
await _progress($"Upload size {_contentSize}", 0);

_ = Invite(ct);

DateTime startTime = DateTime.UtcNow;
TimeSpan timeout = TimeSpan.FromSeconds(30);

while (DateTime.UtcNow - startTime < timeout)
while (true)
{
var retryPolicy = Policy
.Handle<NoResponseException>()
.WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(1));
if (!await retryPolicy.ExecuteAsync(Invite))
return false;

await _progress("Waiting for device to connect...", 0);
var remainingTime = timeout - (DateTime.UtcNow - startTime);
if (remainingTime <= TimeSpan.Zero) break;

var acceptTask = listener.AcceptTcpClientAsync(ct).AsTask();
var delayTask = Task.Delay(10000, ct);

var completedTask = await Task.WhenAny(acceptTask, delayTask);

if (completedTask != acceptTask) continue;
using var client = await acceptTask;
if (await Handle(client)) return true;
var t = await Task.WhenAny(acceptTask, Task.Delay(remainingTime, ct));
if (t == acceptTask)
{
using var client = await acceptTask;
if (await Handle(client)) return true;
}
}

throw new NoResponseException();
Expand All @@ -84,64 +71,96 @@ public async Task<bool> Update(string remoteIp, int remotePort, EspOtaCommand co

async Task<bool> Handle(TcpClient client)
{
var stream = client.GetStream();

var ip = client.Client.RemoteEndPoint as IPEndPoint;
await _progress($"Connection from {ip?.Address}", 2);
var offset = 0;
var chunk = new byte[1460];
var readCount = 0;

while (_contentSize > offset)
using var s = client.GetStream();
using var sr = new StreamReader(s, Encoding.UTF8);
var response = "";
var reader = Task.Run(async () =>
{
var chunkSize = await _fs.ReadAsync(chunk, 0, 1460, ct);
offset += chunkSize;
if (client.Available > 0)
while (true)
{
var r1 = Encoding.UTF8.GetString(chunk, 0, readCount);
Console.Write(r1);
var buf = new char[32];
var r = await sr.ReadAsync(buf, 0, buf.Length);
if (r == 0) break;
var s = new string(buf, 0, r);
foreach (var c in s)
if (!char.IsDigit(c))
response += c;
}
}, ct);
try
{
var ip = client.Client.RemoteEndPoint as IPEndPoint;
await _progress($"Connection from {ip?.Address}", 2);
var offset = 0;
var chunk = new byte[1460];
while (_contentSize > offset)
{
var chunkSize = await _fs.ReadAsync(chunk, 0, 1460, ct);
offset += chunkSize;
await s.WriteAsync(chunk, 0, chunkSize, ct);
await _progress($"Written {offset} out of {_contentSize}", 2 + (int)(offset * 98.0f / _contentSize));
}

await _progress($"Written {offset} out of {_contentSize}", 2 + (int)(offset * 98.0f / _contentSize));
await stream.WriteAsync(chunk, 0, chunkSize, ct);
}

readCount = await stream.ReadAsync(chunk, 0, 1460, ct);
var resp = Encoding.UTF8.GetString(chunk, 0, readCount);
while (!resp.Contains("O"))
finally
{
if (resp.Contains("E"))
return false;

readCount = await stream.ReadAsync(chunk, 0, 1460, ct);
resp = Encoding.UTF8.GetString(chunk, 0, readCount);
Console.Write(resp);
await reader;
}

await _progress("All done!", 100);
client.Close();
return true;
DateTime startTime = DateTime.UtcNow;
await _progress("Response: " + response, 100);
if (response.Contains('E')) return false;
if (response.Contains('O'))
{
await _progress("Successfully Updated!", 100);
return true;
}
return false;
}

async Task<bool> Invite()
async Task<bool> Invite(CancellationToken ct)
{
var message = $"{command:D} {_localPort} {_contentSize} {_fileMd5}\n";
var messageBytes = Encoding.UTF8.GetBytes(message);
await _progress($"Sending invitation to {remoteIp}", 0);

using var udp = new UdpClient();
var ep = new IPEndPoint(IPAddress.Parse(remoteIp), remotePort);
await udp.SendAsync(messageBytes, messageBytes.Length, ep);

var res = udp.ReceiveAsync(ct).AsTask();
var index = await Task.WhenAny(res, Task.Delay(10000, ct));
if (res != index)
throw new NoResponseException();

var resText = Encoding.UTF8.GetString((await res).Buffer);
await _progress($"Invitation: {resText}", 1);

return resText == "OK" ? true : throw new Exception("AUTH required and not implemented");
try
{
using var udp = new UdpClient();
udp.Client.Bind(new IPEndPoint(IPAddress.Any, 0));
var message = $"{command:D} {_localPort} {_contentSize} {_fileMd5}\n";
var messageBytes = Encoding.UTF8.GetBytes(message);
CancellationTokenSource cts = new();
var r = Task.Factory.StartNew(async a =>
{
while (!cts.IsCancellationRequested)
{
var ep = new IPEndPoint(IPAddress.Parse(remoteIp), remotePort);
await udp.SendAsync(messageBytes, messageBytes.Length, ep);
await _progress($"Sent invitation to {remoteIp}", 0);
await Task.Delay(1000, cts.Token);
}
}, cts.Token, TaskCreationOptions.LongRunning);

try
{
var res = udp.ReceiveAsync(ct).AsTask();
var t = await Task.WhenAny(res, Task.Delay(30000, ct));
if (res == t)
{
var resText = Encoding.UTF8.GetString((await res).Buffer);
await _progress($"Invitation: {resText}", 1);
if (resText == "OK")
return true;
throw new Exception("AUTH required and not implemented");
}
throw new NoResponseException();
}
finally
{
cts.Cancel();
await r;
}
}
catch (Exception ex)
{
await _progress($"Error: {ex.Message}", 100);
throw;
}
}
}
}
Expand Down

0 comments on commit 332ead3

Please sign in to comment.