Skip to content

Commit c6cd370

Browse files
authored
Merge pull request #57 from dotnet-campus/t/lindexi/Ipc
添加 IPC 的支持,减少进程启动
2 parents 03d2162 + 7208ad9 commit c6cd370

19 files changed

Lines changed: 474 additions & 37 deletions
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using System.Text.Json;
2+
using System.Text.Json.Serialization;
3+
using dotnetCampus.Ipc.IpcRouteds.DirectRouteds;
4+
using DotNetCampus.MediaConverters.CommandLineHandlers;
5+
using DotNetCampus.MediaConverters.Contexts;
6+
using DotNetCampus.MediaConverters.Contexts.IpcContexts;
7+
8+
namespace DotNetCampus.MediaConverters.Tests.Tool;
9+
10+
[TestClass]
11+
public class MediaConverterIpcTests
12+
{
13+
[TestMethod]
14+
public async Task TestBatchImage()
15+
{
16+
var testFolder = Path.Join(TestHelper.WorkingDirectory.FullName, Path.GetRandomFileName());
17+
Directory.CreateDirectory(testFolder);
18+
19+
var ipcHandler = new IpcHandler()
20+
{
21+
IpcName = Guid.NewGuid().ToString(),
22+
WorkingFolder = testFolder,
23+
ShouldLogToConsole = true,
24+
ShouldLogToFile = true,
25+
};
26+
27+
var task = Task.Run(async () =>
28+
{
29+
var provider = new JsonIpcDirectRoutedProvider();
30+
var clientProxy = await provider.GetAndConnectClientAsync(ipcHandler.IpcName);
31+
32+
var response = await clientProxy.GetResponseAsync<IpcConvertImageResponse>(IpcPaths.RequestConvertImage, new IpcConvertImageRequest()
33+
{
34+
TraceId = "TraceId-1",
35+
InputFile = TestFileProvider.GetTestFile(TestFileProvider.DefaultTestImageName).FullName,
36+
OutputFile = Path.Join(testFolder, "Output1.png"),
37+
ConvertConfigurationFile = ToConfigurationFile(new ImageConvertContext()
38+
{
39+
ImageConvertTaskList =
40+
[
41+
new SetSoftEdgeEffectTask()
42+
{
43+
Radius = 20
44+
}
45+
]
46+
},testFolder)
47+
});
48+
49+
Assert.IsNotNull(response);
50+
Assert.AreEqual(MediaConverterErrorCode.Success.Code, response.Code);
51+
52+
response = await clientProxy.GetResponseAsync<IpcConvertImageResponse>(IpcPaths.RequestConvertImage, new IpcConvertImageRequest()
53+
{
54+
TraceId = "TraceId-2",
55+
InputFile = TestFileProvider.GetTestFile(TestFileProvider.DefaultTestImageName).FullName,
56+
OutputFile = Path.Join(testFolder, "Output2.png"),
57+
ConvertConfigurationFile = ToConfigurationFile(new ImageConvertContext()
58+
{
59+
ImageConvertTaskList =
60+
[
61+
new SetLuminanceEffectTask()
62+
],
63+
ShouldCopyNewFile = false,
64+
}, testFolder)
65+
});
66+
67+
Assert.IsNotNull(response);
68+
Assert.AreEqual(MediaConverterErrorCode.Success.Code, response.Code);
69+
70+
response = await clientProxy.GetResponseAsync<IpcConvertImageResponse>(IpcPaths.RequestConvertImage, new IpcConvertImageRequest()
71+
{
72+
TraceId = "TraceId-3",
73+
InputFile = TestFileProvider.GetTestFile(TestFileProvider.DefaultTestImageName).FullName,
74+
OutputFile = Path.Join(testFolder, "Output3.png"),
75+
ConvertConfigurationFile = ToConfigurationFile(new ImageConvertContext()
76+
{
77+
ImageConvertTaskList =
78+
[
79+
new SetContrastTask()
80+
{
81+
Percentage = 0.7f
82+
}
83+
]
84+
}, testFolder)
85+
});
86+
87+
Assert.IsNotNull(response);
88+
Assert.AreEqual(MediaConverterErrorCode.Success.Code, response.Code);
89+
90+
var ipcExitResponse = await clientProxy.GetResponseAsync<IpcExitResponse>(IpcPaths.Exit, new IpcExitRequest()
91+
{
92+
Reason = "正常退出"
93+
});
94+
Assert.IsNotNull(ipcExitResponse);
95+
Assert.AreEqual(MediaConverterErrorCode.Success.Code, response.Code);
96+
});
97+
98+
await ipcHandler.RunAsync();
99+
100+
await task;
101+
}
102+
103+
private static string ToConfigurationFile(ImageConvertContext imageConvertContext,string testFolder)
104+
{
105+
var jsonText = JsonSerializer.Serialize(imageConvertContext,
106+
new JsonSerializerOptions(MediaConverterJsonSerializerSourceGenerationContext.Default.Options)
107+
{
108+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
109+
});
110+
var configFile = Path.Join(testFolder, "Config.json");
111+
File.WriteAllText(configFile, jsonText);
112+
return configFile;
113+
}
114+
}

src/MediaConverters/MediaConverters.Tests/Tool/MediaConverterTests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System.Text.Json;
22
using System.Text.Json.Serialization;
3-
3+
using DotNetCampus.MediaConverters.CommandLineHandlers;
44
using DotNetCampus.MediaConverters.Contexts;
55
using DotNetCampus.MediaConverters.Imaging.Effects;
66
using DotNetCampus.MediaConverters.Imaging.Effects.Colors;
@@ -253,9 +253,9 @@ public async Task ReplaceColorTask3()
253253
TestHelper.OpenFileInExplorer(new FileInfo(options.OutputFile));
254254
}
255255

256-
private void AssertReplaceColor(Options options)
256+
private void AssertReplaceColor(ConvertHandler convertHandler)
257257
{
258-
var inputFile = options.InputFile;
258+
var inputFile = convertHandler.InputFile;
259259
using var image = Image.Load<Rgba32>(inputFile);
260260

261261
var list = image.GetColorCountList();
@@ -270,14 +270,14 @@ private void AssertReplaceColor(Options options)
270270

271271
image.ReplaceColor(replaceList);
272272

273-
var tempFile = Path.Join(options.WorkingFolder, $"Assert_{Path.GetRandomFileName()}.png");
273+
var tempFile = Path.Join(convertHandler.WorkingFolder, $"Assert_{Path.GetRandomFileName()}.png");
274274
image.SaveAsPng(tempFile, new PngEncoder()
275275
{
276276
ColorType = PngColorType.RgbWithAlpha
277277
});
278278

279279
var visionComparer = new VisionComparer();
280-
var visionCompareResult = visionComparer.Compare(new FileInfo(tempFile), new FileInfo(options.OutputFile));
280+
var visionCompareResult = visionComparer.Compare(new FileInfo(tempFile), new FileInfo(convertHandler.OutputFile));
281281
Assert.IsTrue(visionCompareResult.IsSimilar());
282282
}
283283

@@ -336,7 +336,7 @@ public async Task OptimizeImageFile5()
336336
TestHelper.OpenFileInExplorer(new FileInfo(options.OutputFile));
337337
}
338338

339-
private Options ToOptions(string fileName, ImageConvertContext imageConvertContext)
339+
private ConvertHandler ToOptions(string fileName, ImageConvertContext imageConvertContext)
340340
{
341341
var testFolder = Path.Join(TestHelper.WorkingDirectory.FullName, Path.GetRandomFileName());
342342
Directory.CreateDirectory(testFolder);
@@ -353,7 +353,7 @@ private Options ToOptions(string fileName, ImageConvertContext imageConvertConte
353353
});
354354
File.WriteAllText(configFile, jsonText);
355355

356-
return new Options()
356+
return new ConvertHandler()
357357
{
358358
WorkingFolder = workingFolder,
359359
InputFile = inputFile.FullName,
@@ -380,7 +380,7 @@ public async Task CustomTest()
380380
var workingFolder = Path.Join(testFolder, "Working");
381381
var outputFile = Path.Join(testFolder, "Output.png");
382382

383-
var options = new Options()
383+
var options = new ConvertHandler()
384384
{
385385
WorkingFolder = workingFolder,
386386
InputFile = imageFile,

src/MediaConverters/MediaConverters.Tool.ContextNuGet/MediaConverters.Tool.ContextNuGet.csproj

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,8 @@
1414
</PropertyGroup>
1515

1616
<ItemGroup>
17-
<Compile Include="..\MediaConverters.Tool\Contexts\*.cs" />
17+
<Compile Include="..\MediaConverters.Tool\Contexts\**\*.cs" />
1818
<None Include="..\README.md" Link="README.md" Pack="True" PackagePath="\" />
1919
</ItemGroup>
2020

21-
<ItemGroup>
22-
<Compile Remove="..\MediaConverters.Tool\Contexts\Options.cs" />
23-
</ItemGroup>
24-
2521
</Project>

src/MediaConverters/MediaConverters.Tool/Contexts/Options.cs renamed to src/MediaConverters/MediaConverters.Tool/CommandLineHandlers/ConvertHandler.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
using DotNetCampus.Cli.Compiler;
1+
using System.Threading.Tasks;
2+
using DotNetCampus.Cli;
3+
using DotNetCampus.Cli.Compiler;
24

3-
namespace DotNetCampus.MediaConverters.Contexts;
5+
namespace DotNetCampus.MediaConverters.CommandLineHandlers;
46

5-
public class Options
7+
[Verb("convert")]
8+
public class ConvertHandler : ICommandHandler
69
{
710
[Option]
811
public required string WorkingFolder { get; init; }
@@ -16,6 +19,14 @@ public class Options
1619
[Option]
1720
public required string ConvertConfigurationFile { get; init; }
1821

22+
[Option]
1923
public bool? ShouldLogToConsole { get; init; }
24+
25+
[Option]
2026
public bool? ShouldLogToFile { get; init; }
27+
28+
public async Task<int> RunAsync()
29+
{
30+
return await Program.RunAsync(this);
31+
}
2132
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
using System;
2+
using dotnetCampus.Ipc.Context;
3+
using dotnetCampus.Ipc.IpcRouteds.DirectRouteds;
4+
using dotnetCampus.Ipc.Pipes;
5+
using dotnetCampus.Ipc.Threading;
6+
using dotnetCampus.Ipc.Utils.Buffers;
7+
8+
using DotNetCampus.Cli;
9+
using DotNetCampus.Cli.Compiler;
10+
using DotNetCampus.MediaConverters.Contexts;
11+
using DotNetCampus.MediaConverters.Contexts.IpcContexts;
12+
13+
using System.Buffers;
14+
using System.IO;
15+
using System.Threading;
16+
using System.Threading.Tasks;
17+
18+
namespace DotNetCampus.MediaConverters.CommandLineHandlers;
19+
20+
public class IpcHandler : ICommandHandler
21+
{
22+
[Option]
23+
public required string IpcName { get; init; }
24+
25+
/// <summary>
26+
/// 总的工作路径,可用在后续的转换过程中,存放日志或过程文件等
27+
/// </summary>
28+
[Option]
29+
public required string WorkingFolder { get; init; }
30+
31+
[Option]
32+
public bool? ShouldLogToConsole { get; init; }
33+
34+
[Option]
35+
public bool? ShouldLogToFile { get; init; }
36+
37+
public async Task<int> RunAsync()
38+
{
39+
var ipcConfiguration = new IpcConfiguration()
40+
{
41+
AutoReconnectPeers = false,
42+
IpcLoggerProvider = name => new MediaConverterIpcLogger(name, this),
43+
44+
// 以下为默认配置
45+
SharedArrayPool = new SharedArrayPool(ArrayPool<byte>.Shared),
46+
IpcTaskScheduling = IpcTaskScheduling.GlobalConcurrent,
47+
};
48+
ipcConfiguration.UseSystemJsonIpcObjectSerializer(MediaConverterJsonSerializerSourceGenerationContext.Default);
49+
50+
var ipcProvider = new IpcProvider(IpcName, ipcConfiguration);
51+
52+
var ipcHandlerLogger = new IpcHandlerLogger(this);
53+
var exitTaskCompletionSource = new TaskCompletionSource<int>();
54+
55+
var jsonIpcDirectRoutedProvider = new JsonIpcDirectRoutedProvider(ipcProvider);
56+
57+
jsonIpcDirectRoutedProvider.AddRequestHandler(IpcPaths.RequestConvertImage, async (IpcConvertImageRequest request) =>
58+
{
59+
if (request.InputFile is null || request.OutputFile is null || request.ConvertConfigurationFile is null)
60+
{
61+
return IpcConvertImageResponse.FromErrorCode(MediaConverterErrorCode.InvalidIpcRequestArgument);
62+
}
63+
64+
var traceId = request.TraceId ?? Guid.NewGuid().ToString();
65+
66+
var workingFolder = request.WorkingFolder;
67+
if (string.IsNullOrEmpty(workingFolder))
68+
{
69+
workingFolder = Path.Join(WorkingFolder, traceId);
70+
}
71+
72+
Directory.CreateDirectory(workingFolder);
73+
74+
ipcHandlerLogger.LogMessage($"[{traceId}] Receive RequestConvertImage. InputFile='{request.InputFile}' OutputFile='{request.OutputFile}' ConvertConfigurationFile='{request.ConvertConfigurationFile}' WorkingFolder='{workingFolder}'");
75+
76+
var convertHandler = new ConvertHandler()
77+
{
78+
InputFile = request.InputFile,
79+
OutputFile = request.OutputFile,
80+
ConvertConfigurationFile = request.ConvertConfigurationFile,
81+
WorkingFolder = workingFolder,
82+
ShouldLogToConsole = ShouldLogToConsole,
83+
ShouldLogToFile = ShouldLogToFile,
84+
};
85+
86+
var errorCode = await Program.RunAsync(convertHandler);
87+
ipcHandlerLogger.LogMessage($"[{traceId}] RequestConvertImage completed. ErrorCode={errorCode.Code} Message={errorCode.Message}");
88+
return IpcConvertImageResponse.FromErrorCode(errorCode);
89+
});
90+
91+
jsonIpcDirectRoutedProvider.AddRequestHandler(IpcPaths.Exit, (IpcExitRequest request) =>
92+
{
93+
ipcHandlerLogger.LogMessage($"Request Exit. Code={request.ExitCode} Reason={request.Reason}");
94+
95+
Task.Run(async () =>
96+
{
97+
await Task.Delay(TimeSpan.FromSeconds(1));
98+
exitTaskCompletionSource.SetResult(request.ExitCode);
99+
});
100+
101+
return new IpcExitResponse()
102+
{
103+
Code = MediaConverterErrorCode.Success.Code,
104+
Message = MediaConverterErrorCode.Success.Message
105+
};
106+
});
107+
108+
jsonIpcDirectRoutedProvider.StartServer();
109+
return await exitTaskCompletionSource.Task;
110+
}
111+
}
112+
113+
file class IpcHandlerLogger
114+
{
115+
public IpcHandlerLogger(IpcHandler ipcHandler)
116+
{
117+
_ipcHandler = ipcHandler;
118+
119+
CanLog = _ipcHandler.ShouldLogToConsole is true || _ipcHandler.ShouldLogToFile is true;
120+
121+
if (_ipcHandler.ShouldLogToFile is true)
122+
{
123+
var logFile = Path.Join(_ipcHandler.WorkingFolder, "Log.txt");
124+
_logFile = new FileInfo(logFile);
125+
}
126+
}
127+
128+
private readonly FileInfo? _logFile;
129+
private readonly IpcHandler _ipcHandler;
130+
private readonly Lock _locker = new Lock();
131+
132+
private bool CanLog { get; }
133+
134+
public void LogMessage(string message)
135+
{
136+
if (!CanLog)
137+
{
138+
return;
139+
}
140+
141+
if (_ipcHandler.ShouldLogToConsole is true)
142+
{
143+
Console.WriteLine(message);
144+
}
145+
146+
if (_logFile is { } logFile)
147+
{
148+
var logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss,fff}] {message}";
149+
lock (_locker)
150+
{
151+
File.AppendAllLines(logFile.FullName, [logMessage]);
152+
}
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)