cpu-loader 0.1.3__tar.gz → 0.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/MANIFEST.in +1 -1
  2. {cpu_loader-0.1.3/src/cpu_loader.egg-info → cpu_loader-0.2.4}/PKG-INFO +1 -1
  3. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/pyproject.toml +4 -1
  4. cpu_loader-0.2.4/src/cpu_loader/templates/index.html +456 -0
  5. {cpu_loader-0.1.3 → cpu_loader-0.2.4/src/cpu_loader.egg-info}/PKG-INFO +1 -1
  6. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader.egg-info/SOURCES.txt +2 -1
  7. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/LICENSE +0 -0
  8. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/README.md +0 -0
  9. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/setup.cfg +0 -0
  10. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/setup.py +0 -0
  11. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader/__init__.py +0 -0
  12. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader/__main__.py +0 -0
  13. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader/cpu_loader.py +0 -0
  14. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader/cpu_loader_core.c +0 -0
  15. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader/example.py +0 -0
  16. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader/main.py +0 -0
  17. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader/mqtt_publisher.py +0 -0
  18. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader.egg-info/dependency_links.txt +0 -0
  19. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader.egg-info/entry_points.txt +0 -0
  20. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader.egg-info/requires.txt +0 -0
  21. {cpu_loader-0.1.3 → cpu_loader-0.2.4}/src/cpu_loader.egg-info/top_level.txt +0 -0
@@ -2,4 +2,4 @@ include README.md
2
2
  include LICENSE
3
3
  include pyproject.toml
4
4
  include setup.py
5
- recursive-include src *.py *.c *.h
5
+ recursive-include src *.py *.c *.h *.html
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cpu-loader
3
- Version: 0.1.3
3
+ Version: 0.2.4
4
4
  Summary: A tool to generate CPU load on a system with runtime configuration through a WebUI and REST API
5
5
  Author-email: the78mole <the78mole@users.noreply.github.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cpu-loader"
3
- version = "0.1.3"
3
+ version = "0.2.4"
4
4
  description = "A tool to generate CPU load on a system with runtime configuration through a WebUI and REST API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.8"
@@ -52,3 +52,6 @@ build-backend = "setuptools.build_meta"
52
52
  [tool.setuptools]
53
53
  package-dir = {"" = "src"}
54
54
  packages = ["cpu_loader"]
55
+
56
+ [tool.setuptools.package-data]
57
+ cpu_loader = ["templates/*.html", "*.c"]
@@ -0,0 +1,456 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>CPU Loader Control</title>
7
+ <style>
8
+ body {
9
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10
+ max-width: 900px;
11
+ margin: 50px auto;
12
+ padding: 20px;
13
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
14
+ min-height: 100vh;
15
+ }
16
+ .container {
17
+ background: white;
18
+ border-radius: 15px;
19
+ padding: 30px;
20
+ box-shadow: 0 10px 40px rgba(0,0,0,0.2);
21
+ }
22
+ h1 {
23
+ color: #333;
24
+ text-align: center;
25
+ margin-bottom: 10px;
26
+ }
27
+ .subtitle {
28
+ text-align: center;
29
+ color: #666;
30
+ margin-bottom: 30px;
31
+ }
32
+ .status {
33
+ background: #f0f0f0;
34
+ padding: 15px;
35
+ border-radius: 8px;
36
+ margin-bottom: 25px;
37
+ text-align: center;
38
+ }
39
+ .status-item {
40
+ display: inline-block;
41
+ margin: 0 15px;
42
+ }
43
+ .cpu-metrics {
44
+ background: #f8f9fa;
45
+ padding: 20px;
46
+ border-radius: 10px;
47
+ margin-bottom: 25px;
48
+ }
49
+ .cpu-metrics h3 {
50
+ margin: 0 0 15px 0;
51
+ color: #333;
52
+ font-size: 16px;
53
+ }
54
+ .cpu-bar-container {
55
+ margin-bottom: 12px;
56
+ }
57
+ .cpu-bar-label {
58
+ display: flex;
59
+ justify-content: space-between;
60
+ margin-bottom: 5px;
61
+ font-size: 13px;
62
+ color: #666;
63
+ }
64
+ .cpu-bar-wrapper {
65
+ background: #e0e0e0;
66
+ border-radius: 8px;
67
+ height: 20px;
68
+ overflow: hidden;
69
+ position: relative;
70
+ }
71
+ .cpu-bar {
72
+ background: linear-gradient(90deg, #4CAF50 0%, #FFC107 50%, #F44336 100%);
73
+ height: 100%;
74
+ transition: width 0.3s ease;
75
+ border-radius: 8px;
76
+ }
77
+ .cpu-bar.total {
78
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
79
+ }
80
+ .preset-buttons {
81
+ display: flex;
82
+ gap: 10px;
83
+ margin-bottom: 30px;
84
+ flex-wrap: wrap;
85
+ justify-content: center;
86
+ }
87
+ .preset-btn {
88
+ background: #667eea;
89
+ color: white;
90
+ border: none;
91
+ padding: 12px 20px;
92
+ border-radius: 8px;
93
+ cursor: pointer;
94
+ font-size: 14px;
95
+ font-weight: 600;
96
+ transition: all 0.3s;
97
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
98
+ }
99
+ .preset-btn:hover {
100
+ background: #5568d3;
101
+ transform: translateY(-2px);
102
+ box-shadow: 0 6px 12px rgba(0,0,0,0.15);
103
+ }
104
+ .preset-btn:active {
105
+ transform: translateY(0);
106
+ }
107
+ .thread-controls {
108
+ display: grid;
109
+ gap: 20px;
110
+ }
111
+ .thread-control {
112
+ background: #f8f9fa;
113
+ padding: 20px;
114
+ border-radius: 10px;
115
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
116
+ }
117
+ .thread-header {
118
+ display: flex;
119
+ justify-content: space-between;
120
+ align-items: center;
121
+ margin-bottom: 12px;
122
+ }
123
+ .thread-label {
124
+ font-weight: 600;
125
+ color: #333;
126
+ font-size: 16px;
127
+ }
128
+ .thread-value {
129
+ background: #667eea;
130
+ color: white;
131
+ padding: 4px 12px;
132
+ border-radius: 15px;
133
+ font-weight: 600;
134
+ min-width: 60px;
135
+ text-align: center;
136
+ }
137
+ .slider-container {
138
+ position: relative;
139
+ }
140
+ .slider {
141
+ width: 100%;
142
+ height: 8px;
143
+ border-radius: 5px;
144
+ background: #ddd;
145
+ outline: none;
146
+ -webkit-appearance: none;
147
+ }
148
+ .slider::-webkit-slider-thumb {
149
+ -webkit-appearance: none;
150
+ appearance: none;
151
+ width: 24px;
152
+ height: 24px;
153
+ border-radius: 50%;
154
+ background: #667eea;
155
+ cursor: pointer;
156
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
157
+ }
158
+ .slider::-moz-range-thumb {
159
+ width: 24px;
160
+ height: 24px;
161
+ border-radius: 50%;
162
+ background: #667eea;
163
+ cursor: pointer;
164
+ border: none;
165
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
166
+ }
167
+ .slider-labels {
168
+ display: flex;
169
+ justify-content: space-between;
170
+ margin-top: 5px;
171
+ font-size: 12px;
172
+ color: #666;
173
+ }
174
+ .error {
175
+ background: #fee;
176
+ color: #c33;
177
+ padding: 10px;
178
+ border-radius: 5px;
179
+ margin-bottom: 20px;
180
+ display: none;
181
+ }
182
+ .success {
183
+ background: #efe;
184
+ color: #3c3;
185
+ padding: 10px;
186
+ border-radius: 5px;
187
+ margin-bottom: 20px;
188
+ display: none;
189
+ }
190
+ </style>
191
+ </head>
192
+ <body>
193
+ <div class="container">
194
+ <h1>&#x1F525; CPU Loader Control</h1>
195
+ <p class="subtitle">Control CPU load per thread in real-time</p>
196
+
197
+ <div class="status">
198
+ <div class="status-item">
199
+ <strong>Active Threads:</strong> <span id="thread-count">-</span>
200
+ </div>
201
+ <div class="status-item">
202
+ <strong>Target Load:</strong> <span id="avg-load">-</span>%
203
+ </div>
204
+ </div>
205
+
206
+ <div class="cpu-metrics">
207
+ <h3>📊 Real-Time CPU Usage</h3>
208
+ <div class="cpu-bar-container">
209
+ <div class="cpu-bar-label">
210
+ <span><strong>Total CPU</strong></span>
211
+ <span id="total-cpu-value">-</span>
212
+ </div>
213
+ <div class="cpu-bar-wrapper">
214
+ <div class="cpu-bar total" id="total-cpu-bar" style="width: 0%"></div>
215
+ </div>
216
+ </div>
217
+ <div id="per-cpu-bars"></div>
218
+ </div>
219
+
220
+ <div class="error" id="error-message"></div>
221
+ <div class="success" id="success-message"></div>
222
+
223
+ <div class="preset-buttons">
224
+ <button class="preset-btn" onclick="setAllLoads(0)">0%</button>
225
+ <button class="preset-btn" onclick="setAllLoads(10)">10%</button>
226
+ <button class="preset-btn" onclick="setAllLoads(25)">25%</button>
227
+ <button class="preset-btn" onclick="setAllLoads(50)">50%</button>
228
+ <button class="preset-btn" onclick="setAllLoads(80)">80%</button>
229
+ <button class="preset-btn" onclick="setAllLoads(90)">90%</button>
230
+ <button class="preset-btn" onclick="setAllLoads(100)">100%</button>
231
+ </div>
232
+
233
+ <div id="thread-controls" class="thread-controls">
234
+ <!-- Thread controls will be dynamically inserted here -->
235
+ </div>
236
+ </div>
237
+
238
+ <script>
239
+ let numThreads = 0;
240
+ let updateTimeout = null;
241
+ let ws = null;
242
+ let numCPUs = 0;
243
+
244
+ function connectWebSocket() {
245
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
246
+ const wsUrl = `${protocol}//${window.location.host}/ws/cpu-metrics`;
247
+
248
+ ws = new WebSocket(wsUrl);
249
+
250
+ ws.onopen = () => {
251
+ console.log('WebSocket connected');
252
+ };
253
+
254
+ ws.onmessage = (event) => {
255
+ const data = JSON.parse(event.data);
256
+ if (data.type === 'cpu_metrics') {
257
+ updateCPUMetrics(data);
258
+ }
259
+ };
260
+
261
+ ws.onerror = (error) => {
262
+ console.error('WebSocket error:', error);
263
+ };
264
+
265
+ ws.onclose = () => {
266
+ console.log('WebSocket disconnected, reconnecting...');
267
+ setTimeout(connectWebSocket, 2000);
268
+ };
269
+ }
270
+
271
+ function updateCPUMetrics(data) {
272
+ // Update total CPU
273
+ const totalCpu = data.total_cpu_percent;
274
+ document.getElementById('total-cpu-value').textContent = `${totalCpu}%`;
275
+ document.getElementById('total-cpu-bar').style.width = `${totalCpu}%`;
276
+
277
+ // Update per-CPU bars
278
+ const perCpuContainer = document.getElementById('per-cpu-bars');
279
+ if (data.per_cpu_percent.length !== numCPUs) {
280
+ numCPUs = data.per_cpu_percent.length;
281
+ // Recreate all CPU bars
282
+ perCpuContainer.innerHTML = '';
283
+ data.per_cpu_percent.forEach((cpu, index) => {
284
+ const barHtml = `
285
+ <div class="cpu-bar-container">
286
+ <div class="cpu-bar-label">
287
+ <span>CPU ${index}</span>
288
+ <span id="cpu-${index}-value">-</span>
289
+ </div>
290
+ <div class="cpu-bar-wrapper">
291
+ <div class="cpu-bar" id="cpu-${index}-bar" style="width: 0%"></div>
292
+ </div>
293
+ </div>
294
+ `;
295
+ perCpuContainer.insertAdjacentHTML('beforeend', barHtml);
296
+ });
297
+ }
298
+
299
+ // Update values
300
+ data.per_cpu_percent.forEach((cpu, index) => {
301
+ document.getElementById(`cpu-${index}-value`).textContent = `${cpu}%`;
302
+ document.getElementById(`cpu-${index}-bar`).style.width = `${cpu}%`;
303
+ });
304
+ }
305
+
306
+ async function fetchStatus() {
307
+ try {
308
+ const response = await fetch('/api/threads');
309
+ const data = await response.json();
310
+
311
+ numThreads = data.num_threads;
312
+ document.getElementById('thread-count').textContent = numThreads;
313
+
314
+ // Calculate average load
315
+ const loads = Object.values(data.loads);
316
+ const avgLoad = loads.length > 0
317
+ ? (loads.reduce((a, b) => a + b, 0) / loads.length).toFixed(1)
318
+ : 0;
319
+ document.getElementById('avg-load').textContent = avgLoad;
320
+
321
+ // Update or create thread controls
322
+ updateThreadControls(data.loads);
323
+ } catch (error) {
324
+ showError('Failed to fetch status: ' + error.message);
325
+ }
326
+ }
327
+
328
+ function updateThreadControls(loads) {
329
+ const container = document.getElementById('thread-controls');
330
+
331
+ // If number of threads changed, recreate all controls
332
+ if (container.children.length !== Object.keys(loads).length) {
333
+ container.innerHTML = '';
334
+
335
+ for (let i = 0; i < Object.keys(loads).length; i++) {
336
+ const control = createThreadControl(i, loads[i]);
337
+ container.appendChild(control);
338
+ }
339
+ } else {
340
+ // Just update values
341
+ Object.entries(loads).forEach(([threadId, load]) => {
342
+ const slider = document.getElementById(`slider-${threadId}`);
343
+ const value = document.getElementById(`value-${threadId}`);
344
+ if (slider && value) {
345
+ slider.value = load;
346
+ value.textContent = load.toFixed(0) + '%';
347
+ }
348
+ });
349
+ }
350
+ }
351
+
352
+ function createThreadControl(threadId, initialLoad) {
353
+ const div = document.createElement('div');
354
+ div.className = 'thread-control';
355
+ div.innerHTML = `
356
+ <div class="thread-header">
357
+ <span class="thread-label">Thread ${threadId}</span>
358
+ <span class="thread-value" id="value-${threadId}">
359
+ ${initialLoad.toFixed(0)}%
360
+ </span>
361
+ </div>
362
+ <div class="slider-container">
363
+ <input type="range" min="0" max="100" value="${initialLoad}"
364
+ class="slider" id="slider-${threadId}"
365
+ oninput="updateThreadLoad(${threadId}, this.value)">
366
+ <div class="slider-labels">
367
+ <span>0%</span>
368
+ <span>25%</span>
369
+ <span>50%</span>
370
+ <span>75%</span>
371
+ <span>100%</span>
372
+ </div>
373
+ </div>
374
+ `;
375
+ return div;
376
+ }
377
+
378
+ async function updateThreadLoad(threadId, loadPercent) {
379
+ // Update UI immediately for responsiveness
380
+ document.getElementById(`value-${threadId}`).textContent = loadPercent + '%';
381
+
382
+ // Debounce API calls
383
+ if (updateTimeout) {
384
+ clearTimeout(updateTimeout);
385
+ }
386
+
387
+ updateTimeout = setTimeout(async () => {
388
+ try {
389
+ const response = await fetch(`/api/threads/${threadId}/load`, {
390
+ method: 'PUT',
391
+ headers: {
392
+ 'Content-Type': 'application/json',
393
+ },
394
+ body: JSON.stringify({ load_percent: parseFloat(loadPercent) })
395
+ });
396
+
397
+ if (!response.ok) {
398
+ throw new Error('Failed to update thread load');
399
+ }
400
+
401
+ // Refresh status to get accurate average
402
+ await fetchStatus();
403
+ } catch (error) {
404
+ showError('Failed to update thread ' + threadId + ': ' + error.message);
405
+ }
406
+ }, 100);
407
+ }
408
+
409
+ async function setAllLoads(loadPercent) {
410
+ try {
411
+ const response = await fetch('/api/threads/load/all', {
412
+ method: 'POST',
413
+ headers: {
414
+ 'Content-Type': 'application/json',
415
+ },
416
+ body: JSON.stringify({ load_percent: loadPercent })
417
+ });
418
+
419
+ if (!response.ok) {
420
+ throw new Error('Failed to set loads');
421
+ }
422
+
423
+ showSuccess(`All threads set to ${loadPercent}%`);
424
+
425
+ // Update UI
426
+ await fetchStatus();
427
+ } catch (error) {
428
+ showError('Failed to set all loads: ' + error.message);
429
+ }
430
+ }
431
+
432
+ function showError(message) {
433
+ const errorDiv = document.getElementById('error-message');
434
+ errorDiv.textContent = message;
435
+ errorDiv.style.display = 'block';
436
+ setTimeout(() => {
437
+ errorDiv.style.display = 'none';
438
+ }, 5000);
439
+ }
440
+
441
+ function showSuccess(message) {
442
+ const successDiv = document.getElementById('success-message');
443
+ successDiv.textContent = message;
444
+ successDiv.style.display = 'block';
445
+ setTimeout(() => {
446
+ successDiv.style.display = 'none';
447
+ }, 3000);
448
+ }
449
+
450
+ // Initial load and periodic refresh
451
+ fetchStatus();
452
+ connectWebSocket();
453
+ setInterval(fetchStatus, 5000);
454
+ </script>
455
+ </body>
456
+ </html>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cpu-loader
3
- Version: 0.1.3
3
+ Version: 0.2.4
4
4
  Summary: A tool to generate CPU load on a system with runtime configuration through a WebUI and REST API
5
5
  Author-email: the78mole <the78mole@users.noreply.github.com>
6
6
  License: MIT
@@ -15,4 +15,5 @@ src/cpu_loader.egg-info/SOURCES.txt
15
15
  src/cpu_loader.egg-info/dependency_links.txt
16
16
  src/cpu_loader.egg-info/entry_points.txt
17
17
  src/cpu_loader.egg-info/requires.txt
18
- src/cpu_loader.egg-info/top_level.txt
18
+ src/cpu_loader.egg-info/top_level.txt
19
+ src/cpu_loader/templates/index.html
File without changes
File without changes
File without changes
File without changes