Passed
Push — master ( 96ef05...f81447 )
by Darko
09:59
created

TmuxMonitorService::updateDisplay()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 9
rs 10
cc 2
nc 2
nop 0
1
<?php
2
3
namespace App\Services\Tmux;
4
5
use App\Models\Category;
6
use App\Models\Collection;
7
use App\Models\Release;
8
use App\Models\Settings;
9
use Blacklight\Tmux;
10
use Illuminate\Support\Facades\DB;
11
12
/**
13
 * Service for monitoring tmux operations and collecting statistics
14
 */
15
class TmuxMonitorService
16
{
17
    protected Tmux $tmux;
18
19
    protected array $runVar = [];
20
21
    protected int $iterations = 1;
22
23
    protected bool $shouldContinue = true;
24
25
    public function __construct()
26
    {
27
        $this->tmux = new Tmux;
28
    }
29
30
    /**
31
     * Initialize monitor with default values
32
     */
33
    public function initializeMonitor(): array
34
    {
35
        $this->runVar['paths']['misc'] = base_path().'/misc/';
36
        $this->runVar['paths']['cli'] = base_path().'/cli/';
37
        $this->runVar['paths']['scraper'] = base_path().'/misc/IRCScraper/scrape.php';
38
39
        $this->runVar['constants'] = $this->tmux->getConstantSettings();
40
        $this->runVar['settings'] = $this->tmux->getMonitorSettings();
41
        $this->runVar['connections'] = $this->tmux->getConnectionsInfo($this->runVar['constants']);
42
43
        // Initialize timers
44
        $this->runVar['timers'] = $this->initializeTimers();
45
46
        // Initialize counts
47
        $this->runVar['counts'] = [
48
            'iterations' => 1,
49
            'now' => [],
50
            'start' => [],
51
            'diff' => [],
52
            'percent' => [],
53
        ];
54
55
        return $this->runVar;
56
    }
57
58
    /**
59
     * Initialize all timers
60
     */
61
    protected function initializeTimers(): array
62
    {
63
        $now = time();
64
65
        return [
66
            'timer1' => $now,
67
            'timer2' => $now,
68
            'timer3' => $now,
69
            'timer4' => $now,
70
            'timer5' => $now,
71
            'query' => [
72
                'tmux_time' => 0,
73
                'split_time' => 0,
74
                'init_time' => 0,
75
                'proc1_time' => 0,
76
                'proc2_time' => 0,
77
                'proc3_time' => 0,
78
                'split1_time' => 0,
79
                'init1_time' => 0,
80
                'proc11_time' => 0,
81
                'proc21_time' => 0,
82
                'proc31_time' => 0,
83
                'tpg_time' => 0,
84
                'tpg1_time' => 0,
85
            ],
86
            'newOld' => [
87
                'newestrelname' => '',
88
                'oldestcollection' => 0,
89
                'newestpre' => 0,
90
                'newestrelease' => 0,
91
            ],
92
        ];
93
    }
94
95
    /**
96
     * Collect current statistics
97
     */
98
    public function collectStatistics(): array
99
    {
100
        // Refresh settings periodically
101
        $monitorDelay = (int) ($this->runVar['settings']['monitor'] ?? 60);
102
        $timeSinceLastRefresh = time() - ($this->runVar['timers']['timer2'] ?? 0);
103
104
        if ($this->iterations === 1 || $timeSinceLastRefresh >= $monitorDelay) {
105
            $this->refreshStatistics();
106
            $this->runVar['timers']['timer2'] = time();
107
        }
108
109
        // Update connection counts
110
        $this->updateConnectionCounts();
111
112
        // Set killswitches
113
        $this->setKillswitches();
114
115
        return $this->runVar;
116
    }
117
118
    /**
119
     * Refresh all statistics from database
120
     */
121
    protected function refreshStatistics(): void
122
    {
123
        $timer = time();
124
125
        // Refresh settings
126
        $this->runVar['settings'] = $this->tmux->getMonitorSettings();
127
        $this->runVar['timers']['query']['tmux_time'] = time() - $timer;
128
129
        // Get category counts
130
        $this->getCategoryCounts();
131
132
        // Get process counts
133
        $this->getProcessCounts();
134
135
        // Get table counts
136
        $this->getTableCounts();
137
138
        // Calculate diffs and percentages
139
        $this->calculateStatistics();
140
    }
141
142
    /**
143
     * Get counts by category
144
     */
145
    protected function getCategoryCounts(): void
146
    {
147
        $timer = time();
148
149
        $this->runVar['counts']['now']['tv'] = Release::query()
150
            ->whereBetween('categories_id', [Category::TV_ROOT, Category::TV_OTHER])
151
            ->count('id');
152
153
        $this->runVar['counts']['now']['movies'] = Release::query()
154
            ->whereBetween('categories_id', [Category::MOVIE_ROOT, Category::MOVIE_OTHER])
155
            ->count('id');
156
157
        $this->runVar['counts']['now']['audio'] = Release::query()
158
            ->whereBetween('categories_id', [Category::MUSIC_ROOT, Category::MUSIC_OTHER])
159
            ->count('id');
160
161
        $this->runVar['counts']['now']['books'] = Release::query()
162
            ->whereBetween('categories_id', [Category::BOOKS_ROOT, Category::BOOKS_UNKNOWN])
163
            ->count('id');
164
165
        $this->runVar['counts']['now']['console'] = Release::query()
166
            ->whereBetween('categories_id', [Category::GAME_ROOT, Category::GAME_OTHER])
167
            ->count('id');
168
169
        $this->runVar['counts']['now']['pc'] = Release::query()
170
            ->whereBetween('categories_id', [Category::PC_ROOT, Category::PC_PHONE_ANDROID])
171
            ->count('id');
172
173
        $this->runVar['counts']['now']['xxx'] = Release::query()
174
            ->whereBetween('categories_id', [Category::XXX_ROOT, Category::XXX_OTHER])
175
            ->count('id');
176
177
        $this->runVar['counts']['now']['misc'] = Release::query()
178
            ->whereBetween('categories_id', [Category::OTHER_ROOT, Category::OTHER_HASHED])
179
            ->count('id');
180
181
        $this->runVar['timers']['query']['init_time'] = time() - $timer;
182
    }
183
184
    /**
185
     * Get process-related counts
186
     */
187
    protected function getProcessCounts(): void
188
    {
189
        $timer = time();
190
191
        try {
192
            $bookReqIds = $this->runVar['settings']['book_reqids'] ?? Category::BOOKS_ROOT;
193
            $dbName = config('nntmux.db_name');
194
195
            $proc1Query = $this->tmux->proc_query(1, $bookReqIds, $dbName);
196
            $proc1Result = DB::selectOne($proc1Query);
0 ignored issues
show
Bug introduced by
It seems like $proc1Query can also be of type false; however, parameter $query of Illuminate\Support\Facades\DB::selectOne() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

196
            $proc1Result = DB::selectOne(/** @scrutinizer ignore-type */ $proc1Query);
Loading history...
197
198
            if ($proc1Result) {
199
                foreach ((array) $proc1Result as $key => $value) {
200
                    $this->runVar['counts']['now'][$key] = $value;
201
                }
202
            }
203
204
            $this->runVar['timers']['query']['proc1_time'] = time() - $timer;
205
206
            // Process 2
207
            $timer2 = time();
208
            $maxSize = $this->runVar['settings']['maxsize_pp'] ?? '';
209
            $minSize = $this->runVar['settings']['minsize_pp'] ?? '';
210
211
            $proc2Query = $this->tmux->proc_query(2, $bookReqIds, $dbName, $maxSize, $minSize);
212
            $proc2Result = DB::selectOne($proc2Query);
213
214
            if ($proc2Result) {
215
                foreach ((array) $proc2Result as $key => $value) {
216
                    $this->runVar['counts']['now'][$key] = $value;
217
                }
218
            }
219
220
            $this->runVar['timers']['query']['proc2_time'] = time() - $timer2;
221
222
        } catch (\Exception $e) {
223
            logger()->error('Error collecting process counts: '.$e->getMessage());
224
        }
225
    }
226
227
    /**
228
     * Get table row counts
229
     */
230
    protected function getTableCounts(): void
231
    {
232
        $timer = time();
233
234
        try {
235
            $this->runVar['counts']['now']['collections_table'] = Collection::query()->count();
236
237
            // Get binaries/parts counts from information_schema or use approximation
238
            $dbName = config('nntmux.db_name');
239
            $tables = $this->tmux->cbpmTableQuery();
240
241
            $this->runVar['counts']['now']['binaries_table'] = 0;
242
            $this->runVar['counts']['now']['parts_table'] = 0;
243
            $this->runVar['counts']['now']['missed_parts_table'] = 0;
244
245
            foreach ($tables as $table) {
246
                $tableName = $table->name;
247
                $count = $this->getTableRowCount($tableName);
248
249
                if (str_contains($tableName, 'binaries')) {
250
                    $this->runVar['counts']['now']['binaries_table'] += $count;
251
                } elseif (str_contains($tableName, 'missed_parts')) {
252
                    $this->runVar['counts']['now']['missed_parts_table'] += $count;
253
                } elseif (str_contains($tableName, 'parts')) {
254
                    $this->runVar['counts']['now']['parts_table'] += $count;
255
                }
256
            }
257
258
            $this->runVar['timers']['query']['tpg_time'] = time() - $timer;
259
260
            // Get additional table counts (query 4)
261
            $timer4 = time();
0 ignored issues
show
Unused Code introduced by
The assignment to $timer4 is dead and can be removed.
Loading history...
262
            $bookReqIds = $this->runVar['settings']['book_reqids'] ?? Category::BOOKS_ROOT;
263
            $proc4Query = $this->tmux->proc_query(4, $bookReqIds, $dbName);
264
            $proc4Result = DB::selectOne($proc4Query);
0 ignored issues
show
Bug introduced by
It seems like $proc4Query can also be of type false; however, parameter $query of Illuminate\Support\Facades\DB::selectOne() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

264
            $proc4Result = DB::selectOne(/** @scrutinizer ignore-type */ $proc4Query);
Loading history...
265
266
            if ($proc4Result) {
267
                foreach ((array) $proc4Result as $key => $value) {
268
                    $this->runVar['counts']['now'][$key] = $value;
269
                }
270
            }
271
272
            // Get newest/oldest data (query 6)
273
            $timer6 = time();
0 ignored issues
show
Unused Code introduced by
The assignment to $timer6 is dead and can be removed.
Loading history...
274
            $proc6Query = $this->tmux->proc_query(6, $bookReqIds, $dbName);
275
            $proc6Result = DB::selectOne($proc6Query);
276
277
            if ($proc6Result) {
278
                foreach ((array) $proc6Result as $key => $value) {
279
                    $this->runVar['timers']['newOld'][$key] = $value;
280
                }
281
            }
282
283
        } catch (\Exception $e) {
284
            logger()->error('Error collecting table counts: '.$e->getMessage());
285
        }
286
    }
287
288
    /**
289
     * Get row count for a table
290
     */
291
    protected function getTableRowCount(string $tableName): int
292
    {
293
        try {
294
            $result = DB::selectOne(
295
                'SELECT TABLE_ROWS AS count FROM information_schema.TABLES
296
                 WHERE TABLE_NAME = ? AND TABLE_SCHEMA = DATABASE()',
297
                [$tableName]
298
            );
299
300
            return (int) ($result->count ?? 0);
301
        } catch (\Exception $e) {
302
            return 0;
303
        }
304
    }
305
306
    /**
307
     * Calculate statistics (diffs, percentages, totals)
308
     */
309
    protected function calculateStatistics(): void
310
    {
311
        // Calculate total work
312
        $this->runVar['counts']['now']['total_work'] = 0;
313
314
        foreach ($this->runVar['counts']['now'] as $key => $value) {
315
            if (str_starts_with($key, 'process')) {
316
                $this->runVar['counts']['now']['total_work'] += $value;
317
            }
318
        }
319
320
        // Set initial start values on first iteration
321
        if ($this->iterations === 1) {
322
            $this->runVar['counts']['start'] = $this->runVar['counts']['now'];
323
        }
324
325
        // Calculate diffs and percentages
326
        $totalReleases = $this->runVar['counts']['now']['releases'] ?? 1;
327
328
        foreach ($this->runVar['counts']['now'] as $key => $value) {
329
            $startValue = $this->runVar['counts']['start'][$key] ?? 0;
330
            $this->runVar['counts']['diff'][$key] = number_format($value - $startValue);
331
            $this->runVar['counts']['percent'][$key] = $totalReleases > 0
332
                ? sprintf('%02d', floor(($value / $totalReleases) * 100))
333
                : 0;
334
        }
335
    }
336
337
    /**
338
     * Update connection counts
339
     */
340
    protected function updateConnectionCounts(): void
341
    {
342
        $this->runVar['conncounts'] = $this->tmux->getUSPConnections(
343
            'primary',
344
            $this->runVar['connections']
345
        );
346
347
        if ((int) ($this->runVar['constants']['alternate_nntp'] ?? 0) === 1) {
348
            $alternateConns = $this->tmux->getUSPConnections(
349
                'alternate',
350
                $this->runVar['connections']
351
            );
352
            $this->runVar['conncounts'] = array_merge($this->runVar['conncounts'], $alternateConns);
353
        }
354
    }
355
356
    /**
357
     * Set killswitches based on limits
358
     */
359
    protected function setKillswitches(): void
360
    {
361
        $ppKillLimit = (int) ($this->runVar['settings']['postprocess_kill'] ?? 0);
362
        $collKillLimit = (int) ($this->runVar['settings']['collections_kill'] ?? 0);
363
364
        $totalWork = (int) ($this->runVar['counts']['now']['total_work'] ?? 0);
365
        $collections = (int) ($this->runVar['counts']['now']['collections_table'] ?? 0);
366
367
        $this->runVar['killswitch']['pp'] = ($ppKillLimit > 0 && $ppKillLimit < $totalWork);
368
        $this->runVar['killswitch']['coll'] = ($collKillLimit > 0 && $collKillLimit < $collections);
369
    }
370
371
    /**
372
     * Update the monitor display
373
     */
374
    public function updateDisplay(): void
375
    {
376
        // This would output to terminal - implementation depends on TmuxOutput
377
        // For now, we'll just log basic info
378
        if ($this->iterations % 10 === 0) {
379
            logger()->info('Tmux Monitor', [
380
                'iteration' => $this->iterations,
381
                'total_work' => $this->runVar['counts']['now']['total_work'] ?? 0,
382
                'collections' => $this->runVar['counts']['now']['collections_table'] ?? 0,
383
            ]);
384
        }
385
    }
386
387
    /**
388
     * Increment iteration counter
389
     */
390
    public function incrementIteration(): void
391
    {
392
        $this->iterations++;
393
        $this->runVar['counts']['iterations'] = $this->iterations;
394
    }
395
396
    /**
397
     * Check if monitoring should continue
398
     */
399
    public function shouldContinue(): bool
400
    {
401
        $exitFlag = (int) Settings::settingValue('exit');
402
403
        if ($exitFlag === 0) {
404
            return true;
405
        }
406
407
        $this->shouldContinue = false;
408
409
        return false;
410
    }
411
412
    /**
413
     * Get current run variables
414
     */
415
    public function getRunVar(): array
416
    {
417
        return $this->runVar;
418
    }
419
}
420