From 66d94ced8a9c68ee2178be24b131f90e71be4940 Mon Sep 17 00:00:00 2001 From: Ahmed Zamil Date: Tue, 25 Jun 2024 14:47:28 +0300 Subject: [PATCH] up --- AZ.Compute.Agent/AZ.Compute.Agent.csproj | 16 + AZ.Compute.Agent/App.cs | 28 + AZ.Compute.Agent/Job.cs | 53 ++ AZ.Compute.Agent/Node.cs | 122 +++++ AZ.Compute.sln | 8 +- AZ.Compute/AZ.Compute.csproj | 11 + AZ.Compute/App.cs | 60 ++- AZ.Compute/Properties/launchSettings.json | 8 + AZ.Compute/Service.cs | 33 ++ AZ.Compute/Web/css/style.css | 501 ++++++++++++++++++ AZ.Compute/Web/img/coie.jpg | Bin 0 -> 14567 bytes AZ.Compute/Web/index.html | 112 ++++ AZ.Compute/Web/js/app.js | 48 ++ AZ.Compute/Web/package-lock.json | 252 +++++++++ AZ.Compute/Web/package.json | 6 + .../AZ.Compute.Agent.Job.Generated.cs | 67 +++ .../AZ.Compute.Agent.Node.Generated.cs | 72 +++ AZ.Compute/localhost_10001/Esiur.Generated.cs | 8 + 18 files changed, 1402 insertions(+), 3 deletions(-) create mode 100644 AZ.Compute.Agent/AZ.Compute.Agent.csproj create mode 100644 AZ.Compute.Agent/App.cs create mode 100644 AZ.Compute.Agent/Job.cs create mode 100644 AZ.Compute.Agent/Node.cs create mode 100644 AZ.Compute/Properties/launchSettings.json create mode 100644 AZ.Compute/Service.cs create mode 100644 AZ.Compute/Web/css/style.css create mode 100644 AZ.Compute/Web/img/coie.jpg create mode 100644 AZ.Compute/Web/index.html create mode 100644 AZ.Compute/Web/js/app.js create mode 100644 AZ.Compute/Web/package-lock.json create mode 100644 AZ.Compute/Web/package.json create mode 100644 AZ.Compute/localhost_10001/AZ.Compute.Agent.Job.Generated.cs create mode 100644 AZ.Compute/localhost_10001/AZ.Compute.Agent.Node.Generated.cs create mode 100644 AZ.Compute/localhost_10001/Esiur.Generated.cs diff --git a/AZ.Compute.Agent/AZ.Compute.Agent.csproj b/AZ.Compute.Agent/AZ.Compute.Agent.csproj new file mode 100644 index 0000000..ae63786 --- /dev/null +++ b/AZ.Compute.Agent/AZ.Compute.Agent.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + diff --git a/AZ.Compute.Agent/App.cs b/AZ.Compute.Agent/App.cs new file mode 100644 index 0000000..ed25137 --- /dev/null +++ b/AZ.Compute.Agent/App.cs @@ -0,0 +1,28 @@ + +using AZ.Compute.Agent; +using Esiur.Net.IIP; +using Esiur.Resource; +using Esiur.Stores; + +internal class App +{ + + + private static async Task Main(string[] args) + { + // Create a store to keep objects. + var system = await Warehouse.Put("sys", new MemoryStore()); + // Create a distibuted server + var esiurServer = await Warehouse.Put("sys/server", new DistributedServer() { Port = 10001}); + + // Add your object to the store + var service = await Warehouse.Put("sys/agent", new Node()); + + + // Start your server + await Warehouse.Open(); + + Console.WriteLine("AZ Compute Agent is running."); + + } +} \ No newline at end of file diff --git a/AZ.Compute.Agent/Job.cs b/AZ.Compute.Agent/Job.cs new file mode 100644 index 0000000..859568b --- /dev/null +++ b/AZ.Compute.Agent/Job.cs @@ -0,0 +1,53 @@ +using Esiur.Resource; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AZ.Compute.Agent +{ + [Resource] + public partial class Job + { + [Export] int id; + [Export] string name; + [Export] string command; + [Export] DateTime start = DateTime.Now; + [Export] DateTime finish; + [Export] bool finished; + [Export] object results; + + [Export] float cpu; + [Export] float ram; + + Process process; + + public Process Process + { + get => process; + set + { + process = value; + Name = process.ProcessName; + Command = process.StartInfo.Arguments; + Id = process.Id; + process.Exited += Process_Exited; + } + } + + private void Process_Exited(object? sender, EventArgs e) + { + finished = true; + finish = DateTime.Now; + } + + [Export] + public void Kill() + { + process?.Kill(); + } + + } +} diff --git a/AZ.Compute.Agent/Node.cs b/AZ.Compute.Agent/Node.cs new file mode 100644 index 0000000..dd26d2b --- /dev/null +++ b/AZ.Compute.Agent/Node.cs @@ -0,0 +1,122 @@ +using Esiur.Core; +using Esiur.Data; +using Esiur.Resource; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Management; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Text; +using System.Timers; + +namespace AZ.Compute.Agent +{ + [Resource] + public partial class Node + { + + System.Timers.Timer timer; + PerformanceCounter cpuCounter; + PerformanceCounter ramCounter; + NetworkInterface nic; + ulong totalNetworkBytes; + + + [Export] string id; + [Export] string ip; + + [Export] float cpu; + [Export] float ram; + [Export] float network; + + [Export] uint tasks; + + [Export] uint cpuClock; + [Export] uint cpuMaxClock; + + [Export] float networkSpeed; + + [Export] Job[] jobs=new Job[0]; + + + [Export] + public async AsyncReply Compute(string fileName, string arguments) + { + var psi = new ProcessStartInfo() + { + FileName = fileName, + Arguments = arguments + }; + + var p = Process.Start(psi); + + var job = await Warehouse.New(fileName, null, this); + job.Process = p; + + + Jobs = jobs.Append(job).ToArray(); + + return job; + } + + public Node() + { + + Id = System.Environment.MachineName; + + nic = NetworkInterface.GetAllNetworkInterfaces().Where(x => x.OperationalStatus == OperationalStatus.Up).First(); + networkSpeed = (float)(nic.Speed / 1048576.0); + + totalNetworkBytes = (ulong)nic.GetIPv4Statistics().BytesReceived + (ulong)nic.GetIPv4Statistics().BytesSent; + + Ip = nic.GetIPProperties().UnicastAddresses.First(x=> x.Address.AddressFamily == AddressFamily.InterNetwork).Address.ToString(); + + + using (ManagementObject Mo = new ManagementObject("Win32_Processor.DeviceID='CPU0'")) + { + cpuClock = (uint)(Mo["CurrentClockSpeed"]); + cpuMaxClock = (uint)(Mo["MaxClockSpeed"]); + } + + //foreach (ManagementObject obj in new ManagementObjectSearcher("SELECT *, Name FROM Win32_Processor").Get()) + //{ + // double maxSpeed = Convert.ToDouble(obj["MaxClockSpeed"]) / 1000; + // double turboSpeed = maxSpeed * cpuValue / 100; + // return string.Format("{0} Running at {1:0.00}Ghz, Turbo Speed: {2:0.00}Ghz", obj["Name"], maxSpeed, turboSpeed); + //} + + cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); + ramCounter = new PerformanceCounter("Memory", "Available MBytes"); + + timer = new System.Timers.Timer(); + timer.Elapsed += Timer_Elapsed; + timer.Interval = 5000; + timer.Start(); + } + + private void Timer_Elapsed(object? sender, ElapsedEventArgs e) + { + try + { + + var ntb = (ulong)nic.GetIPv4Statistics().BytesReceived + (ulong)nic.GetIPv4Statistics().BytesSent; + + network = (float)(((ntb - totalNetworkBytes) / 5.0) / nic.Speed); + + Cpu = cpuCounter.NextValue(); + Ram = ramCounter.NextValue(); + + foreach (Job job in jobs) + { + job.Ram = (float)(job.Process.PeakWorkingSet64 / 1048576.0); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + } + } +} diff --git a/AZ.Compute.sln b/AZ.Compute.sln index c97c00c..39519b9 100644 --- a/AZ.Compute.sln +++ b/AZ.Compute.sln @@ -3,13 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34714.143 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AZ.Compute", "AZ.Compute\AZ.Compute.csproj", "{6AA91B86-D066-4ADF-81E6-97A483ECBE25}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AZ.Compute", "AZ.Compute\AZ.Compute.csproj", "{6AA91B86-D066-4ADF-81E6-97A483ECBE25}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8BA307C4-5641-405A-9B5B-36954FB53621}" ProjectSection(SolutionItems) = preProject .gitignore = .gitignore EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AZ.Compute.Agent", "AZ.Compute.Agent\AZ.Compute.Agent.csproj", "{19B16887-A64F-4B55-8FE6-39F3E2C0DFF6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,6 +22,10 @@ Global {6AA91B86-D066-4ADF-81E6-97A483ECBE25}.Debug|Any CPU.Build.0 = Debug|Any CPU {6AA91B86-D066-4ADF-81E6-97A483ECBE25}.Release|Any CPU.ActiveCfg = Release|Any CPU {6AA91B86-D066-4ADF-81E6-97A483ECBE25}.Release|Any CPU.Build.0 = Release|Any CPU + {19B16887-A64F-4B55-8FE6-39F3E2C0DFF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19B16887-A64F-4B55-8FE6-39F3E2C0DFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19B16887-A64F-4B55-8FE6-39F3E2C0DFF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19B16887-A64F-4B55-8FE6-39F3E2C0DFF6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AZ.Compute/AZ.Compute.csproj b/AZ.Compute/AZ.Compute.csproj index 2150e37..2141a91 100644 --- a/AZ.Compute/AZ.Compute.csproj +++ b/AZ.Compute/AZ.Compute.csproj @@ -7,4 +7,15 @@ enable + + + + + + + + + + + diff --git a/AZ.Compute/App.cs b/AZ.Compute/App.cs index 3751555..caf9fd9 100644 --- a/AZ.Compute/App.cs +++ b/AZ.Compute/App.cs @@ -1,2 +1,58 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); + +using AZ.Compute; +using Esiur.Net.HTTP; +using Esiur.Net.IIP; +using Esiur.Resource; +using Esiur.Stores; +using Microsoft.AspNetCore.StaticFiles; + +internal class Program +{ + + static FileExtensionContentTypeProvider MIMEProvider = new FileExtensionContentTypeProvider(); + + private static async Task Main(string[] args) + { + // Create a store to keep objects. + var system = await Warehouse.Put("sys", new MemoryStore()); + // Create a distibuted server + var esiurServer = await Warehouse.Put("sys/server", new DistributedServer()); + // Add your object to the store + var service = await Warehouse.Put("sys/service", new Service()); + + var http = await Warehouse.Put("sys/http", new HTTPServer() { Port = 8888 }); + + + http.MapGet("{url}", (string url, HTTPConnection sender) => + { + var fn = "Web/" + (sender.Request.Filename == "/" ? "/index.html" : sender.Request.Filename); + + if (File.Exists(fn)) + { + + string contentType; + + if (!MIMEProvider.TryGetContentType(fn, out contentType)) + contentType = "application/octet-stream"; + + sender.Response.Headers["Content-Type"] = contentType; + sender.SendFile(fn).Wait(20000); + } + else + { + sender.Response.Number = Esiur.Net.Packets.HTTP.HTTPResponseCode.NotFound; + sender.Send("`" + fn + "` Not Found"); + sender.Close(); + } + + }); + + + // Start your server + await Warehouse.Open(); + + + Console.WriteLine("AZ Compute Engine is running... "); + Console.WriteLine("Web: http://localhost:8888"); + } +} \ No newline at end of file diff --git a/AZ.Compute/Properties/launchSettings.json b/AZ.Compute/Properties/launchSettings.json new file mode 100644 index 0000000..8170f16 --- /dev/null +++ b/AZ.Compute/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "AZ.Compute": { + "commandName": "Project", + "workingDirectory": "." + } + } +} \ No newline at end of file diff --git a/AZ.Compute/Service.cs b/AZ.Compute/Service.cs new file mode 100644 index 0000000..5283c2e --- /dev/null +++ b/AZ.Compute/Service.cs @@ -0,0 +1,33 @@ + +using AZ.Compute.Agent; +using Esiur.Resource; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AZ.Compute +{ + [Resource] + public partial class Service + { + [Export] + Node[] agents = new Node[0]; + + [Export] + public async void AddAgent(string ip) + { + var agent = await Warehouse.Get($"iip://{ip}:10001/sys/agent", new { AutoReconnect = true }); + + Agents = agents.Append(agent).ToArray(); + } + + [Export] + public void Compute(string fileName, string arguments) + { + Agents.First().Compute(fileName, arguments); + + } + } +} diff --git a/AZ.Compute/Web/css/style.css b/AZ.Compute/Web/css/style.css new file mode 100644 index 0000000..5d1b35e --- /dev/null +++ b/AZ.Compute/Web/css/style.css @@ -0,0 +1,501 @@ + +@font-face { + font-family: def; + src: url(/font/Catamaran/Catamaran-Regular.ttf); +} + +html { + font-family: def; +} +body { + overflow: hidden; +} + +i-router { + height: 100vh; + flex: 1; +} + +i-app { + display: flex; + flex-direction: column; + height: 100%; +} + +.logo { + height: 50px; +} + +.label { + padding: 10; + margin: 10; + border: 1px solid #7b7b7b; + border-radius: 10px; + color: #000000; + background: #e3e3e3; + font-size: 32px; + width: calc(100% - 50px); +} + +canvas { + border: 1px solid #b5b5b5; + height: 320px; + width: 400px; + margin: 10px; +} + +i-router { + padding: 10px 35px; + height: calc(100vh - 150px); + overflow: auto; +} + +.content { + display: flex; + flex-direction: column; + padding: 8px; +} + +.iui-error { + display: none; +} + +/* row */ +.row label { + display: flex; + align-items: center; +} + +.row-wrap { + display: flex; + flex-wrap: wrap; +} + + .row-wrap > .row { + flex-grow: 1; + } + + .row-wrap > .row > div { + flex-grow: 1; + align-items: center; + } + + .row-wrap > .row > div > * { + flex: 1; + } + +.row-center { + display: flex; + align-items: center; + gap: 8px; +} + +.row > div:last-child { + border-left: 0; +} + +.row, .row-slim { + border: 1px solid #ddd; + display: flex; +} + + + .row-slim > div, .row-slim > div { + border-left: 1px solid #ddd; + display: flex; + align-items: center; + } + + .row > div, .row > span { + border-left: 1px solid #ddd; + padding: 5px; + display: flex; + align-items: center; + } + + .row > .column, .column > .row { + padding: 0; + } + + .row > span, .column > span { + font-weight: bold; + } + +/* column */ +.column > div, .column > span { + border-bottom: 1px solid #ddd; + padding: 5px; + display: flex; +} + +.column { + display: flex; + flex-direction: column; +} + + .column > div:last-child { + border-bottom: 0; + flex: 1; + } + + +.table-form { + width: 100%; + border-collapse: collapse; +} + + .table-form > tbody > tr > td:nth-child(1) { + /*background-color: #4ebeec; */ + /* color: white; */ + font-weight: bold; + padding: 8px; + width: 120px; + } + + +/* table-list */ +.table-list { + width: 100%; + border-collapse: collapse; +} + + .table-list td, .table-form > tbody > tr > td { + border: 1px solid #ddd; + padding: 8px; + } + + .table-list tr:nth-child(even) { + background-color: #f2f2f2; + } + + .table-list tbody > tr:hover { + background-color: #ddd; + } + + .table-list thead { + font-weight: bold; + padding-top: 12px; + padding-bottom: 12px; + background-color: #4ebeec; + color: white; + } + + .table-list td > img { + max-height: 36px; + max-width: 36px; + } + +/* table-print */ + +.table-print { + border-collapse: collapse; + width: 100%; +} + + .table-print > thead { + font-weight: bold; + text-align: center; + background: #e2e2e2; + } + + .table-print > tbody > tr > td:first-child { + font-weight: bold; + } + + .table-print > tfoot { + background: #e2e2e2; + } + + .table-print td { + padding: 2px; + border: 1px solid #ddd; + } + +/* actions */ + +.actions { + padding: 7px 8px; + background-color: #f1f1f1; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + display: flex; + gap: 3px; +} + +.footer { + height: 42px; + text-align: center; + background: linear-gradient(0deg, #c3c3c3, #f7f7f7); + position: absolute; + width: 100%; + bottom: 0; +} + +.loading-panel { + position: absolute; + z-index: 1001; + width: 100%; + height: 100%; + background: #545454; + align-items: center; + display: flex; + justify-content: center; + font-size: 12px; + box-shadow: inset 0px 0px 9vh 5vh #3e3e3e; + color: #9c9c9c; + transition: all ease-out .5s .2s; +} + +.loading-panel-hidden { + opacity: 0; + visibility: hidden; + transition: all ease-out .5s .2s; +} + + +.title-bar { + display: flex; + background-color: #66788600; + color: black; + height: 60px; + align-items: center; + box-shadow: 0px 1px 3px 1px #dedede; + background: #fff; + margin: 10px; + padding: 5px; +} + + .title-bar h1 { + flex-grow: 1; + font-size: 24px; + margin: 9px; + color: #4ebeec; + } + +i-router { + padding: 10px 35px; + height: calc(100vh - 150px); + overflow: auto; +} + +.loading { + margin: 15vh auto; + display: flex; + font-weight: bold; + /*color: #303030;*/ + align-items: center; + flex-direction: column; +} + + +.app { + transition: margin-left .5s; + display: flex; + flex-direction: column; +} + +.app-shrunk { + margin-left: 250px; +} + +.side-bar { + height: calc(100% - 132px); + width: 0; + position: fixed; + z-index: 1; + top: 81px; + left: 10px; + background-color: #efefef; + transition: 0.5s; + display: flex; + flex-direction: column; + box-shadow: inset -6px 0px 4px 2px #dfdfdf; + overflow: hidden; +} + +html[dir='rtl'] .side-bar { + box-shadow: inset 6px 0px 4px 2px #dfdfdf; +} + +.side-bar i-link { + padding: 4px 8px 4px 4px; + text-decoration: none; + font-size: 15px; + color: #818181; + display: flex; + align-items: center; + height: 27px; +} + +.side-bar span { + width: 100%; +} + + +.side-bar i-link:hover { + color: #4ebeec; + background-color: #f8f8f8; +} + +.side-bar i-link[selected]:hover { + background-color: #4ebeec; + color: #fff; +} + +.side-bar-visible { + width: 240px; +} + + +i-modellist > i-repeat { + max-height: 260px; + overflow-y: scroll; + display: block; + box-shadow: 1px 1px 2px 2px #d4d4d4; + border: white 1px solid; + padding: 10px; + margin: 5px; + margin-bottom: 10px; +} + +.connection-0 { + background: red; +} + + +.connection-1 { + background: yellow; + transition: background 2s; +} + + +.connection-2 { + background: #5de198; + transition: background 2s; +} + +.navbar-item img { + height: 20px; + width: 20px; + padding: 0px 6px 0px 4px; +} + +.link { + display: flex; +} + + +input[type=radio] { + display: none; +} + +input[type=radio]:checked + label span { + transform: scale(1.25); +} + +input[type=radio]:checked + label .red { + border: 2px solid #711313; +} + +input[type=radio]:checked + label .orange { + border: 2px solid #873a08; +} + +input[type=radio]:checked + label .yellow { + border: 2px solid #816102; +} + +input[type=radio]:checked + label .olive { + border: 2px solid #505a0b; +} + +input[type=radio]:checked + label .green { + border: 2px solid #0e4e1d; +} + +input[type=radio]:checked + label .teal { + border: 2px solid #003633; +} + +input[type=radio]:checked + label .blue { + border: 2px solid #103f62; +} + +input[type=radio]:checked + label .violet { + border: 2px solid #321a64; +} + +input[type=radio]:checked + label .purple { + border: 2px solid #501962; +} + +input[type=radio]:checked + label .pink { + border: 2px solid #851554; +} + +label { + display: inline-block; + width: 25px; + height: 25px; + margin-right: 10px; + cursor: pointer; +} + +label:hover span { + transform: scale(1.25); +} + +label span { + display: block; + width: 100%; + height: 100%; + transition: transform 0.2s ease-in-out; +} + +label span.black { + background: #000000; +} + +label span.white { + background: #ffffff; +} + +label span.red { + background: #DB2828; +} + +label span.orange { + background: #F2711C; +} + +label span.yellow { + background: #FBBD08; +} + +label span.olive { + background: #B5CC18; +} + +label span.green { + background: #21BA45; +} + +label span.teal { + background: #00B5AD; +} + +label span.blue { + background: #2185D0; +} + +label span.violet { + background: #6435C9; +} + +label span.purple { + background: #A333C8; +} + +label span.pink { + background: #E03997; +} \ No newline at end of file diff --git a/AZ.Compute/Web/img/coie.jpg b/AZ.Compute/Web/img/coie.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1b0d0f86a02eba4655531d785fd0cdc2276e6142 GIT binary patch literal 14567 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 nxa_uKSb + + + + + + + + + + + + + + + + + + + + + + Esiur Demo + + + + + + + + + + + + + + + + + +
+ +

AZ Compute Engine

+

${FORMAT_CONNECTION_STATUS(d?.status ?? 0)}

+
+
+ +
+ Add Job + Add Agent +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
NodeIPStatusCPURAMNetworkJobs
${d.Id}${d.Ip}${d.Status}${d.Cpu}${d.Ram}${d.Network} + + + + + + + + + + + + + + + + + +
NameStartFinished
${d.Name}${d.Start.toLocaleTimeString()}${d.Finished}
+
+
+
+
+
+ + \ No newline at end of file diff --git a/AZ.Compute/Web/js/app.js b/AZ.Compute/Web/js/app.js new file mode 100644 index 0000000..f97824e --- /dev/null +++ b/AZ.Compute/Web/js/app.js @@ -0,0 +1,48 @@ + +async function init() { + try { + + connection = await wh.get(`iip://${window.location.hostname}`, { + autoReconnect: true + }); + + window.service = await connection.get("sys/service"); + + await app.setData(connection); + console.log(connection); + + + } + catch (ex) + { + alert(ex); + } +} + +async function addJob() { + let cmd = prompt("Job command", ""); + + if (cmd != null) { + try { + await service.Compute(cmd, ""); + } catch (ex) { + alert(ex); + } + } +} + +async function addAgent() { + let ip = prompt("Agent IP or hostname", ""); + + if (ip != null) { + try { + await service.AddAgent(ip); + } catch (ex) { + alert(ex); + } + } +} + +const FORMAT_CONNECTION_STATUS = (x) => ["Offline", "Connecting...", "Online"][x]; + + \ No newline at end of file diff --git a/AZ.Compute/Web/package-lock.json b/AZ.Compute/Web/package-lock.json new file mode 100644 index 0000000..a3a0039 --- /dev/null +++ b/AZ.Compute/Web/package-lock.json @@ -0,0 +1,252 @@ +{ + "name": "Web", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@esiur/iui": "^1.2.1", + "esiur": "^2.3.1" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esiur/iui": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@esiur/iui/-/iui-1.2.1.tgz", + "integrity": "sha512-+fa/rzEBwcDK5J+HFDhLVOMtXqCSoYKXcHQHAqCdcFbhz6xFFeULVPKcBN1K3e8+4N6OEe53tr/Lneu3cX/xeA==" + }, + "node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/bson": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esiur": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/esiur/-/esiur-2.3.1.tgz", + "integrity": "sha512-FHZg32FfpuD6a6qgV/TLrs1GTriXE4i6oMWu8oIoe/P6swgeI52LHF3o1qIgTupCtVl3IAo8IUG0itWAFooRrA==", + "dependencies": { + "@babel/runtime": "^7.20.7", + "mongodb": "^3.6.9", + "ws": "^7.5.0" + }, + "bin": { + "esiur": "bin/esiur.cjs" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "node_modules/mongodb": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.4.tgz", + "integrity": "sha512-K5q8aBqEXMwWdVNh94UQTwZ6BejVbFhh1uB6c5FKtPE9eUMZPUO3sRZdgIEcHSrAWmxzpG/FeODDKL388sqRmw==", + "dependencies": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "optional-require": "^1.1.8", + "safe-buffer": "^5.1.2" + }, + "engines": { + "node": ">=4" + }, + "optionalDependencies": { + "saslprep": "^1.0.0" + }, + "peerDependenciesMeta": { + "aws4": { + "optional": true + }, + "bson-ext": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "mongodb-extjson": { + "optional": true + }, + "snappy": { + "optional": true + } + } + }, + "node_modules/optional-require": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", + "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", + "dependencies": { + "require-at": "^1.0.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/require-at": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", + "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "optional": true, + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/AZ.Compute/Web/package.json b/AZ.Compute/Web/package.json new file mode 100644 index 0000000..3e32fc5 --- /dev/null +++ b/AZ.Compute/Web/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "@esiur/iui": "^1.2.1", + "esiur": "^2.3.1" + } +} diff --git a/AZ.Compute/localhost_10001/AZ.Compute.Agent.Job.Generated.cs b/AZ.Compute/localhost_10001/AZ.Compute.Agent.Job.Generated.cs new file mode 100644 index 0000000..4b72103 --- /dev/null +++ b/AZ.Compute/localhost_10001/AZ.Compute.Agent.Job.Generated.cs @@ -0,0 +1,67 @@ +using System; +using Esiur.Resource; +using Esiur.Core; +using Esiur.Data; +using Esiur.Net.IIP; +namespace AZ.Compute.Agent { +[ClassId("2e7cea0b000aab7ee0f0e98a60298a53")] +public class Job : DistributedResource { +public Job(DistributedConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) {} +public Job() {} +[Annotation("() -> Void")] +[Export] public AsyncReply Kill() { +var args = new Map(){}; +var rt = new AsyncReply(); +_Invoke(0, args) +.Then(x => rt.Trigger((object)x)) +.Error(x => rt.TriggerError(x)) +.Chunk(x => rt.TriggerChunk(x)); +return rt; } +[Annotation("String")] +[Export] public string Command { +get => (string)properties[0]; +set => _SetSync(0, value); +} +[Annotation("Single")] +[Export] public float Cpu { +get => (float)properties[1]; +set => _SetSync(1, value); +} +[Annotation("DateTime")] +[Export] public DateTime Finish { +get => (DateTime)properties[2]; +set => _SetSync(2, value); +} +[Annotation("Boolean")] +[Export] public bool Finished { +get => (bool)properties[3]; +set => _SetSync(3, value); +} +[Annotation("Int32")] +[Export] public int Id { +get => (int)properties[4]; +set => _SetSync(4, value); +} +[Annotation("String")] +[Export] public string Name { +get => (string)properties[5]; +set => _SetSync(5, value); +} +[Annotation("Single")] +[Export] public float Ram { +get => (float)properties[6]; +set => _SetSync(6, value); +} +[Annotation("Object")] +[Export] public object Results { +get => (object)properties[7]; +set => _SetSync(7, value); +} +[Annotation("DateTime")] +[Export] public DateTime Start { +get => (DateTime)properties[8]; +set => _SetSync(8, value); +} + +} +} diff --git a/AZ.Compute/localhost_10001/AZ.Compute.Agent.Node.Generated.cs b/AZ.Compute/localhost_10001/AZ.Compute.Agent.Node.Generated.cs new file mode 100644 index 0000000..5be4e7e --- /dev/null +++ b/AZ.Compute/localhost_10001/AZ.Compute.Agent.Node.Generated.cs @@ -0,0 +1,72 @@ +using System; +using Esiur.Resource; +using Esiur.Core; +using Esiur.Data; +using Esiur.Net.IIP; +namespace AZ.Compute.Agent { +[ClassId("2f73616a78827890a18fddec371f2217")] +public class Node : DistributedResource { +public Node(DistributedConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) {} +public Node() {} +[Annotation("([String] fileName,[String] arguments) -> AsyncReply`1")] +[Export] public AsyncReply Compute(string fileName, string arguments) { +var args = new Map(){[0] = fileName, [1] = arguments}; +var rt = new AsyncReply(); +_Invoke(0, args) +.Then(x => rt.Trigger((AZ.Compute.Agent.Job)x)) +.Error(x => rt.TriggerError(x)) +.Chunk(x => rt.TriggerChunk(x)); +return rt; } +[Annotation("Single")] +[Export] public float Cpu { +get => (float)properties[0]; +set => _SetSync(0, value); +} +[Annotation("UInt32")] +[Export] public uint CpuClock { +get => (uint)properties[1]; +set => _SetSync(1, value); +} +[Annotation("UInt32")] +[Export] public uint CpuMaxClock { +get => (uint)properties[2]; +set => _SetSync(2, value); +} +[Annotation("String")] +[Export] public string Id { +get => (string)properties[3]; +set => _SetSync(3, value); +} +[Annotation("String")] +[Export] public string Ip { +get => (string)properties[4]; +set => _SetSync(4, value); +} +[Annotation("Job[]")] +[Export] public AZ.Compute.Agent.Job[] Jobs { +get => (AZ.Compute.Agent.Job[])properties[5]; +set => _SetSync(5, value); +} +[Annotation("Single")] +[Export] public float Network { +get => (float)properties[6]; +set => _SetSync(6, value); +} +[Annotation("Single")] +[Export] public float NetworkSpeed { +get => (float)properties[7]; +set => _SetSync(7, value); +} +[Annotation("Single")] +[Export] public float Ram { +get => (float)properties[8]; +set => _SetSync(8, value); +} +[Annotation("UInt32")] +[Export] public uint Tasks { +get => (uint)properties[9]; +set => _SetSync(9, value); +} + +} +} diff --git a/AZ.Compute/localhost_10001/Esiur.Generated.cs b/AZ.Compute/localhost_10001/Esiur.Generated.cs new file mode 100644 index 0000000..5f85bac --- /dev/null +++ b/AZ.Compute/localhost_10001/Esiur.Generated.cs @@ -0,0 +1,8 @@ +using System; + namespace Esiur { + public static class Generated { + public static Type[] Resources {get;} = new Type[] {}; + public static Type[] Records { get; } = new Type[] { }; + public static Type[] Enums { get; } = new Type[] { }; + } +} \ No newline at end of file