Skip to content
This repository has been archived by the owner on Sep 3, 2024. It is now read-only.

Commit

Permalink
Merge pull request #66 from julien-wff/feat-remote-client
Browse files Browse the repository at this point in the history
feat: remote client
  • Loading branch information
julien-wff authored Dec 20, 2023
2 parents 0d76e4b + e018243 commit 1237555
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 66 deletions.
43 changes: 38 additions & 5 deletions EasyGUI/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,14 @@ namespace EasyGUI;
/// </summary>
public partial class MainWindow
{
private readonly JobManager _jobManager;
private JobManager _jobManager;

public MainWindow()
{
InitializeComponent();

_jobManager = new LocalJobManager();
foreach (var job in _jobManager.GetJobs())
{
Jobs.Add(job);
}
_refreshJobs();
}

public ObservableCollection<Job> Jobs { get; } = new();
Expand Down Expand Up @@ -196,5 +193,41 @@ private void JobsHeader_OnConnectButtonClick(object sender, RoutedEventArgs e)
private void RemoteConnectPopup_OnConnect(object? sender, RemoteConnectEventArgs e)
{
var endpoint = e.EndPoint;

// Instantiate and configure a new remote job manager
var remoteJobManager = new RemoteJobManager();
remoteJobManager.JobListChanged += _refreshJobs;

// Connect to the remote job manager
var connected = remoteJobManager.Connect(endpoint);
if (!connected)
{
remoteJobManager.JobListChanged -= _refreshJobs;
RemoteConnectPopup.ErrorMessage = Strings.RemoteConnectPopup_Error_HostUnreachable;
return;
}

// Remove the old job manager and delete the jobs
_jobManager.CleanStop();
Jobs.Clear();

// Change the job manager
_jobManager = remoteJobManager;

// Close the popup
RemoteConnectPopup.Visibility = Visibility.Collapsed;
}

private void _refreshJobs(object? sender = null, EventArgs? e = null)
{
var jobs = _jobManager.GetJobs();
Dispatcher.Invoke(() =>
{
Jobs.Clear();
foreach (var job in jobs)
{
Jobs.Add(job);
}
});
}
}
113 changes: 111 additions & 2 deletions EasyLib/Api/JobManagerClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,114 @@
namespace EasyLib.Api;
using System.Net.Sockets;
using System.Text;
using EasyLib.Enums;
using EasyLib.Job;
using EasyLib.JobManager;
using EasyLib.Json;
using Newtonsoft.Json;

public class JobManagerClient
namespace EasyLib.Api;

public class JobManagerClient(RemoteJobManager remoteJobManager, Socket serverSocket)
{
public void Listen()
{
while (true)
{
try
{
var buffer = new byte[2018];
var receivedBytes = serverSocket.Receive(buffer);

if (receivedBytes < 1)
break;

var json = Encoding.UTF8.GetString(buffer, 0, receivedBytes);
_handleAction(json);
}
catch (Exception e)
{
Console.Error.WriteLine(e);
break;
}
}
}

public bool SendAction(ApiAction action, Job.Job job)
{
try
{
var json = JsonConvert.SerializeObject(new JsonApiRequest(action, job, job.CurrentlyRunning)) + "\n\r";
var data = Encoding.ASCII.GetBytes(json);
serverSocket.Send(data);
return true;
}
catch (Exception)
{
return false;
}
}

private void _handleAction(string jsonAction)
{
JsonApiRequest request;
try
{
request = JsonConvert.DeserializeObject<JsonApiRequest>(jsonAction);
}
catch (Exception)
{
return;
}

var job = _createOrUpdateJob(request.Job, request.JobRunning);

switch (request.Action)
{
case ApiAction.Start:
break;
case ApiAction.Pause:
break;
case ApiAction.Resume:
break;
case ApiAction.Cancel:
break;
case ApiAction.Delete:
break;
case ApiAction.Edit:
break;
case ApiAction.Error:
job.OnJobError(new ApplicationException("Unknown error"));
break;
case ApiAction.State:
job.OnJobStateChange(job.State, job);
break;
case ApiAction.Progress:
job.OnJobProgress(job);
break;
case ApiAction.Create:
remoteJobManager.AddJob(job);
break;
default:
return;
}
}

private Job.Job _createOrUpdateJob(JsonJob jsonJob, bool running)
{
var job = remoteJobManager.GetJobs().Find(j => j.Id == jsonJob.id)
?? new RemoteJob(jsonJob, this);
job.Name = jsonJob.name;
job.SourceFolder = jsonJob.source_folder;
job.DestinationFolder = jsonJob.destination_folder;
job.Type = EnumConverter<JobType>.ConvertToEnum(jsonJob.type);
job.State = EnumConverter<JobState>.ConvertToEnum(jsonJob.state);
job.FilesCount = jsonJob.active_job_info?.total_file_count ?? 0;
job.FilesSizeBytes = jsonJob.active_job_info?.total_file_size ?? 0;
job.FilesCopied = jsonJob.active_job_info?.files_copied ?? 0;
job.FilesBytesCopied = jsonJob.active_job_info?.bytes_copied ?? 0;
job.CurrentFileSource = jsonJob.active_job_info?.current_file_source ?? string.Empty;
job.CurrentFileDestination = jsonJob.active_job_info?.current_file_destination ?? string.Empty;
job.CurrentlyRunning = running;
return job;
}
}
19 changes: 12 additions & 7 deletions EasyLib/Api/JobManagerServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,24 @@ public JobManagerServer(LocalJobManager localJobManager)
}
}

public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource();
public CancellationTokenSource CancellationTokenSource { get; } = new();

private void _waitForConnection()
{
try
{
while (true)
{
TcpClient socket = _serverSocket.AcceptTcpClient();
var socket = _serverSocket.AcceptTcpClient();

Worker worker = new Worker(socket, this);
var worker = new Worker(socket, this);
AddWorker(worker);
worker.SendAllJobs(_localJobManager.GetJobs());
if (CancellationTokenSource.IsCancellationRequested)
break;
}
}
catch (SocketException e)
catch (SocketException)
{
CleanInstance();
}
Expand All @@ -67,13 +67,18 @@ public void RemoveWorker(Worker worker)
}
}

public void Broadcast(ApiAction action, JsonJob jsonJob)
public void Broadcast(ApiAction action, JsonJob jsonJob, bool jobRunning)
{
lock (ServerLockObject)
{
foreach (Worker worker in _workers)
foreach (var worker in _workers)
{
worker.Send(new JsonApiRequest() { Action = action, Job = jsonJob });
worker.Send(new JsonApiRequest
{
Action = action,
Job = jsonJob,
JobRunning = jobRunning
});
}
}
}
Expand Down
52 changes: 27 additions & 25 deletions EasyLib/Api/JobManagerServerWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,13 @@
using EasyLib.Enums;
using EasyLib.Job;
using EasyLib.Json;
using Newtonsoft.Json;

namespace EasyLib.Api;

public class Worker
public class Worker(TcpClient socket, JobManagerServer server)
{
private readonly JobManagerServer _server;
private readonly TcpClient _socket;
private readonly Stream _stream;

public Worker(TcpClient socket, JobManagerServer server)
{
this._socket = socket;
this._stream = socket.GetStream();
this._server = server;
}
private readonly Stream _stream = socket.GetStream();

public void Start()
{
Expand All @@ -26,44 +18,54 @@ public void Start()

public void Send(JsonApiRequest request)
{
string json = Newtonsoft.Json.JsonConvert.SerializeObject(request);
byte[] buffer = Encoding.UTF8.GetBytes(json);
var json = JsonConvert.SerializeObject(request, Formatting.None) + "\n\r";
var buffer = Encoding.UTF8.GetBytes(json);
_stream.Write(buffer, 0, buffer.Length);
_stream.Flush();
}

private void Run()
{
try
{
byte[] buffer = new byte[2018];
var buffer = new byte[2018];
while (true)
{
int receivedBytes = _stream.Read(buffer, 0, buffer.Length);
var receivedBytes = _stream.Read(buffer, 0, buffer.Length);

if (receivedBytes < 1)
break;
JsonApiRequest request =
Newtonsoft.Json.JsonConvert.DeserializeObject<JsonApiRequest>(
Encoding.UTF8.GetString(buffer, 0, receivedBytes));
_server.ExecuteJobCommand(request.Action, new LocalJob(request.Job));
if (_server.CancellationTokenSource.IsCancellationRequested)

var request = JsonConvert.DeserializeObject<JsonApiRequest>(
Encoding.UTF8.GetString(buffer, 0, receivedBytes)
);

server.ExecuteJobCommand(request.Action, new LocalJob(request.Job));

if (server.CancellationTokenSource.IsCancellationRequested)
break;
}
}
catch (Exception e)
catch (Exception)
{
Close();
lock (_server.ServerLockObject)
lock (server.ServerLockObject)
{
_server.RemoveWorker(this);
server.RemoveWorker(this);
}
}
}

public void SendAllJobs(List<Job.Job> jobs)
{
foreach (Job.Job job in jobs)
foreach (var job in jobs)
{
Send(new JsonApiRequest() { Action = ApiAction.Create, Job = job.ToJsonJob() });
Send(new JsonApiRequest
{
Action = ApiAction.Create,
Job = job.ToJsonJob(),
JobRunning = job.CurrentlyRunning
});
}
}

Expand Down
2 changes: 1 addition & 1 deletion EasyLib/Job/Job.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public abstract class Job(
/// True if the job is currently running, false otherwise
/// If it's false but the state is not End, it means that the job is paused
/// </summary>
public bool CurrentlyRunning { get; protected set; }
public bool CurrentlyRunning { get; set; }

/// <summary>
/// Subscribers to the job-related events
Expand Down
33 changes: 27 additions & 6 deletions EasyLib/Job/RemoteJob.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,48 @@
using EasyLib.Enums;
using EasyLib.Api;
using EasyLib.Enums;
using EasyLib.Json;

namespace EasyLib.Job;

public class RemoteJob(string name, string source, string destination, JobType type)
public class RemoteJob(string name, string source, string destination, JobType type, JobManagerClient client)
: Job(name, source, destination, type)
{
/// <summary>
/// Create a job instance from a JsonJob object
/// </summary>
/// <param name="job">JsonJob object</param>
/// <param name="client">JobManagerClient instance</param>
public RemoteJob(JsonJob job, JobManagerClient client)
: this(job.name, job.source_folder, job.destination_folder, JobType.Full, client)
{
Id = job.id;
Type = EnumConverter<JobType>.ConvertToEnum(job.type);
State = EnumConverter<JobState>.ConvertToEnum(job.state);
FilesCount = job.active_job_info?.total_file_count ?? 0;
FilesSizeBytes = job.active_job_info?.total_file_size ?? 0;
FilesCopied = job.active_job_info?.files_copied ?? 0;
FilesBytesCopied = job.active_job_info?.bytes_copied ?? 0;
CurrentFileSource = job.active_job_info?.current_file_source ?? string.Empty;
CurrentFileDestination = job.active_job_info?.current_file_destination ?? string.Empty;
}

public override bool Resume()
{
throw new NotImplementedException();
return client.SendAction(ApiAction.Resume, this);
}

public override bool Run()
{
throw new NotImplementedException();
return client.SendAction(ApiAction.Start, this);
}

public override bool Pause()
{
throw new NotImplementedException();
return client.SendAction(ApiAction.Pause, this);
}

public override bool Cancel()
{
throw new NotImplementedException();
return client.SendAction(ApiAction.Cancel, this);
}
}
Loading

0 comments on commit 1237555

Please sign in to comment.