diff --git a/WelsonJS.Toolkit/WelsonJS.Service/HeartbeatClient.cs b/WelsonJS.Toolkit/WelsonJS.Service/HeartbeatClient.cs new file mode 100644 index 0000000..16f0221 --- /dev/null +++ b/WelsonJS.Toolkit/WelsonJS.Service/HeartbeatClient.cs @@ -0,0 +1,125 @@ +// HeartbeatClient.cs +// Namhyeon Go +// https://github.com/gnh1201/welsonjs +using Grpc.Core; +using System.Threading.Tasks; +using System; +using System.Management; +using System.ServiceProcess; +using Grpc.Net.Client; +using Grpc.Net.Client.Web; +using System.Net.Http; + +namespace WelsonJS.Service +{ + public class HeartbeatClient + { + private readonly HeartbeatService.HeartbeatServiceClient _client; + private readonly GrpcChannel _channel; + private const int HeartbeatInterval = 2000; // 2 seconds + private ServiceMain _parent; + private string clientId; + private string serverAddress; + + public HeartbeatClient(ServiceBase parent) + { + _parent = (ServiceMain)parent; + + try + { + serverAddress = _parent.GetSettingsFileHandler().Read("GRPC_HOST", "Service"); + if (String.IsNullOrEmpty(serverAddress)) + { + throw new Exception("The server address could not be empty."); + } + } + catch (Exception ex) + { + serverAddress = "http://localhost:50051"; + _parent.Log($"Failed to read the host address. {ex.Message} Use default value: {serverAddress}"); + } + + var httpClientHandler = new HttpClientHandler(); + var grpcWebHandler = new GrpcWebHandler(GrpcWebMode.GrpcWebText, httpClientHandler); + _channel = GrpcChannel.ForAddress(serverAddress, new GrpcChannelOptions + { + HttpHandler = grpcWebHandler, + Credentials = ChannelCredentials.Insecure + }); + _client = new HeartbeatService.HeartbeatServiceClient(_channel); + + clientId = GetSystemUUID().ToLower(); + _parent.Log($"Use the client ID: {clientId}"); + } + + public async Task StartHeartbeatAsync() + { + var call = _client.CheckHeartbeat(); + try + { + while (true) + { + var request = new HeartbeatRequest { + IsAlive = true + }; + await call.RequestStream.WriteAsync(request); + _parent.Log("Sent heartbeat"); + + if (await call.ResponseStream.MoveNext()) + { + var response = call.ResponseStream.Current; + _parent.Log("Heartbeat response received: " + response.IsAlive); + } + + await Task.Delay(HeartbeatInterval); + } + } + finally + { + await call.RequestStream.CompleteAsync(); + } + } + + public async Task StartEventListenerAsync() + { + var eventRequest = new FetchEventsRequest { + ClientId = clientId + }; + var eventCall = _client.FetchPendingEvents(eventRequest); + try + { + while (await eventCall.ResponseStream.MoveNext()) + { + var response = eventCall.ResponseStream.Current; + _parent.Log($"Received event from server: {response.EventType} with args: {string.Join(", ", response.Args)}"); + } + } + finally {} + } + + public async Task ShutdownAsync() + { + await _channel.ShutdownAsync(); + } + + private string GetSystemUUID() + { + try + { + using (var searcher = new ManagementObjectSearcher("SELECT UUID FROM Win32_ComputerSystemProduct")) + { + foreach (var mo in searcher.Get()) + { + return mo["UUID"].ToString(); + } + } + } + catch (Exception ex) + { + _parent.Log($"An error occurred while retrieving the system UUID: {ex.Message}"); + } + + return "UNKNOWN"; + } + } +} diff --git a/WelsonJS.Toolkit/WelsonJS.Service/Protos/heartbeat.proto b/WelsonJS.Toolkit/WelsonJS.Service/Protos/heartbeat.proto new file mode 100644 index 0000000..0ec3442 --- /dev/null +++ b/WelsonJS.Toolkit/WelsonJS.Service/Protos/heartbeat.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +option csharp_namespace = "WelsonJS.Service"; + +package heartbeat; + +service HeartbeatService { + rpc CheckHeartbeat (stream HeartbeatRequest) returns (stream HeartbeatResponse); + rpc FetchPendingEvents (FetchEventsRequest) returns (stream FetchEventsResponse); +} + +message HeartbeatRequest { + bool is_alive = 1; +} + +message HeartbeatResponse { + bool is_alive = 1; +} + +message FetchEventsRequest { + string client_id = 1; +} + +message FetchEventsResponse { + string event_type = 1; + repeated string args = 2; +} diff --git a/WelsonJS.Toolkit/WelsonJS.Service/ScreenMatch.cs b/WelsonJS.Toolkit/WelsonJS.Service/ScreenMatch.cs index b7dd095..a8a4738 100644 --- a/WelsonJS.Toolkit/WelsonJS.Service/ScreenMatch.cs +++ b/WelsonJS.Toolkit/WelsonJS.Service/ScreenMatch.cs @@ -223,23 +223,26 @@ public class ScreenMatch throw new Exception("Waiting done a previous job..."); } - toggleIsMatching(); - - switch (mode) + if (templateImages.Count > 0) { - case "screen": // 화면 기준 - results = CaptureAndMatchAllScreens(); - toggleIsMatching(); - break; + toggleIsMatching(); - case "window": // 윈도우 핸들 기준 - results = CaptureAndMatchAllWindows(); - toggleIsMatching(); - break; + switch (mode) + { + case "screen": // 화면 기준 + results = CaptureAndMatchAllScreens(); + toggleIsMatching(); + break; - default: - toggleIsMatching(); - throw new Exception($"Unknown capture mode {mode}"); + case "window": // 윈도우 핸들 기준 + results = CaptureAndMatchAllWindows(); + toggleIsMatching(); + break; + + default: + toggleIsMatching(); + throw new Exception($"Unknown capture mode {mode}"); + } } return results; diff --git a/WelsonJS.Toolkit/WelsonJS.Service/ServiceMain.cs b/WelsonJS.Toolkit/WelsonJS.Service/ServiceMain.cs index 1b838fa..79e4efd 100644 --- a/WelsonJS.Toolkit/WelsonJS.Service/ServiceMain.cs +++ b/WelsonJS.Toolkit/WelsonJS.Service/ServiceMain.cs @@ -33,6 +33,7 @@ using System.IO; using System.Collections.Generic; using WelsonJS.TinyINIController; using System.Collections; +using System.Threading.Tasks; namespace WelsonJS.Service { @@ -47,6 +48,7 @@ namespace WelsonJS.Service private readonly string logFilePath = Path.Combine(Path.GetTempPath(), "WelsonJS.Service.Log.txt"); private readonly string appName = "WelsonJS"; private string[] args; + private bool disabledHeartbeat = false; private bool disabledScreenTime = false; private bool disabledFileMonitor = false; private ScreenMatch screenMatcher; @@ -79,6 +81,10 @@ namespace WelsonJS.Service scriptName = entry.Value; break; + case "disable-heartbeat": + disabledHeartbeat = true; + break; + case "disable-screen-time": disabledScreenTime = true; break; @@ -129,9 +135,9 @@ namespace WelsonJS.Service { string[] configNames = new string[] { + "DISABLE_HEARTBEAT", "DISABLE_SCREEN_TIME", - "DISABLE_FILE_MONITOR", - "DISABLE_MESSAGE_RECEIVER" + "DISABLE_FILE_MONITOR" }; foreach (string configName in configNames) { @@ -141,6 +147,10 @@ namespace WelsonJS.Service { switch (configName) { + case "DISABLE_HEARTBEAT": + disabledHeartbeat = true; + break; + case "DISABLE_SCREEN_TIME": disabledScreenTime = true; break; @@ -171,6 +181,14 @@ namespace WelsonJS.Service // set path of the script scriptFilePath = Path.Combine(workingDirectory, "app.js"); + // start the heartbeat + if (!disabledHeartbeat) + { + HeartbeatClient heartbeatClient = new HeartbeatClient(this); + Task.Run(heartbeatClient.StartHeartbeatAsync); + Task.Run(heartbeatClient.StartEventListenerAsync); + } + // set default timer Timer defaultTimer = new Timer { diff --git a/WelsonJS.Toolkit/WelsonJS.Service/WelsonJS.Service.csproj b/WelsonJS.Toolkit/WelsonJS.Service/WelsonJS.Service.csproj index 433da9b..aa2f128 100644 --- a/WelsonJS.Toolkit/WelsonJS.Service/WelsonJS.Service.csproj +++ b/WelsonJS.Toolkit/WelsonJS.Service/WelsonJS.Service.csproj @@ -1,6 +1,6 @@  - + @@ -16,6 +16,21 @@ + 게시\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true AnyCPU @@ -89,9 +104,15 @@ ..\packages\Grpc.Core.2.46.6\lib\net45\Grpc.Core.dll + + ..\packages\Grpc.Core.Api.2.65.0\lib\net462\Grpc.Core.Api.dll + ..\packages\Grpc.Net.Client.2.65.0\lib\net462\Grpc.Net.Client.dll + + ..\packages\Grpc.Net.Client.Web.2.65.0\lib\netstandard2.0\Grpc.Net.Client.Web.dll + ..\packages\Grpc.Net.Common.2.65.0\lib\netstandard2.0\Grpc.Net.Common.dll @@ -109,6 +130,9 @@ ..\packages\RestSharp.111.4.1\lib\net48\RestSharp.dll + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + @@ -116,11 +140,20 @@ + + ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll + ..\packages\System.Net.Http.WinHttpHandler.8.0.2\lib\net462\System.Net.Http.WinHttpHandler.dll + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + ..\packages\System.Text.Encodings.Web.8.0.0\lib\net462\System.Text.Encodings.Web.dll @@ -140,6 +173,7 @@ + Component @@ -186,7 +220,20 @@ - + + False + Microsoft .NET Framework 4.8%28x86 및 x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + @@ -194,10 +241,10 @@ 이 프로젝트는 이 컴퓨터에 없는 NuGet 패키지를 참조합니다. 해당 패키지를 다운로드하려면 NuGet 패키지 복원을 사용하십시오. 자세한 내용은 http://go.microsoft.com/fwlink/?LinkID=322105를 참조하십시오. 누락된 파일은 {0}입니다. - - + + - + \ No newline at end of file diff --git a/WelsonJS.Toolkit/WelsonJS.Service/packages.config b/WelsonJS.Toolkit/WelsonJS.Service/packages.config index 4d11241..8fa8dd6 100644 --- a/WelsonJS.Toolkit/WelsonJS.Service/packages.config +++ b/WelsonJS.Toolkit/WelsonJS.Service/packages.config @@ -3,16 +3,22 @@ + + - + + + + + diff --git a/settings.example.ini b/settings.example.ini index 950e8fb..69c20c7 100644 --- a/settings.example.ini +++ b/settings.example.ini @@ -8,9 +8,9 @@ WINDIVERT_CONFIG_FILE=data/windivert.config.json NONFREE_STRICT=false [Service] -DISABLE_SCREEN_TIME=false -DISABLE_FILE_MONITOR=false -DISABLE_MESSAGE_RECEIVER=false +DISABLE_HEARTBEAT=true +DISABLE_SCREEN_TIME=true +DISABLE_FILE_MONITOR=true ; screen or window SCREEN_TIME_MODE=screen ;filename.exe,backward,save