/
glrbbir
/
ShedulerOS
Обзор
Документация
Войти
/
glrbbir
/
ShedulerOS
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
ui/simulation-visualizer.js
189 строк
7 KB
Gleb
project
12 июн 2026, 19:15
12 июн 2026, 19:15
9180c5a
Код
Авторство
О чём код?
class SimulationVisualizer { constructor() { this.leftPanelWidth = APP_CONFIG.CANVAS_LEFT_PANEL_WIDTH; this.rowHeight = APP_CONFIG.CANVAS_ROW_HEIGHT; this.paddingTop = APP_CONFIG.CANVAS_PADDING_TOP; this.paddingBottom = APP_CONFIG.CANVAS_PADDING_BOTTOM; this.blockHeight = APP_CONFIG.CANVAS_BLOCK_HEIGHT; } draw(canvas, timeline, totalTime, threads = [], currentTime = null) { const ctx = canvas.getContext('2d'); const container = canvas.parentElement; const displayUpTo = (currentTime !== null && currentTime !== undefined) ? currentTime : (timeline.length > 0 ? Math.max(...timeline.map(t => t.endTime)) : 0); let threadIds; if (threads.length > 0) { threadIds = threads.map(t => t.id); [...new Set(timeline.map(t => t.threadId))].forEach(id => { if (!threadIds.includes(id)) threadIds.push(id); }); } else { threadIds = [...new Set(timeline.map(t => t.threadId))]; } const threadMap = new Map(threadIds.map((id, i) => [id, i])); const minHeight = APP_CONFIG.CANVAS_MIN_HEIGHT; const calculatedHeight = this.paddingTop + (threadIds.length * this.rowHeight) + this.paddingBottom; const displayHeight = Math.max(minHeight, calculatedHeight); const displayWidth = container.clientWidth; const dpr = window.devicePixelRatio || 1; canvas.style.width = `${displayWidth}px`; canvas.style.height = `${displayHeight}px`; canvas.width = displayWidth * dpr; canvas.height = displayHeight * dpr; ctx.scale(dpr, dpr); const width = displayWidth; ctx.fillStyle = '#f4f5f7'; ctx.fillRect(0, 0, width, displayHeight); const graphWidth = width - this.leftPanelWidth - 20; const safeTotalTime = Math.max(totalTime || 15, 1); const timePerPixel = graphWidth / safeTotalTime; this.drawGrid(ctx, width, displayHeight, safeTotalTime, timePerPixel, threadIds); if (threads.length > 0) { this.drawIdleBlocks(ctx, timeline, threads, threadMap, timePerPixel, displayUpTo); } timeline.forEach(entry => { const rowIndex = threadMap.get(entry.threadId); this.drawExecutionBlocks(ctx, entry, rowIndex, timePerPixel); }); } /** * Рисует штриховые блоки для всех промежутков ожидания каждого потока. */ drawIdleBlocks(ctx, timeline, threads, threadMap, timePerPixel, displayUpTo) { threads.forEach(thread => { const rowIndex = threadMap.get(thread.id); if (rowIndex === undefined) return; const entries = timeline .filter(t => t.threadId === thread.id) .sort((a, b) => a.startTime - b.startTime); let cursor = thread.arrivalTime; let executedTime = 0; if (entries.length === 0) { this._drawIdleRange(ctx, cursor, displayUpTo, rowIndex, timePerPixel); return; } for (const entry of entries) { if (cursor < entry.startTime) { const gapEnd = Math.min(entry.startTime, displayUpTo); this._drawIdleRange(ctx, cursor, gapEnd, rowIndex, timePerPixel); } cursor = Math.max(cursor, entry.endTime); executedTime += (entry.endTime - entry.startTime); } if (executedTime < thread.burstTime && cursor < displayUpTo) { this._drawIdleRange(ctx, cursor, displayUpTo, rowIndex, timePerPixel); } }); } /** * Рисует штриховые блоки в диапазоне [startTime, endTime) на строке rowIndex */ _drawIdleRange(ctx, startTime, endTime, rowIndex, timePerPixel) { if (endTime <= startTime) return; const y = this.paddingTop + rowIndex * this.rowHeight + (this.rowHeight - this.blockHeight) / 2; for (let t = startTime; t < endTime; t++) { const x = this.leftPanelWidth + t * timePerPixel; ctx.fillStyle = 'rgba(140, 140, 180, 0.10)'; ctx.fillRect(x, y, timePerPixel, this.blockHeight); ctx.save(); ctx.setLineDash([3, 3]); ctx.strokeStyle = '#a0a0c0'; ctx.lineWidth = 1; ctx.strokeRect(x + 0.5, y + 0.5, timePerPixel - 1, this.blockHeight - 1); ctx.restore(); } } drawGrid(ctx, width, height, totalTime, timePerPixel, threadIds) { ctx.lineWidth = 1; ctx.font = '12px Arial'; ctx.textBaseline = 'middle'; const graphBottomY = this.paddingTop + threadIds.length * this.rowHeight; threadIds.forEach((id, i) => { const y = this.paddingTop + i * this.rowHeight; ctx.strokeStyle = '#cccccc'; ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); ctx.fillStyle = '#006666'; ctx.textAlign = 'left'; ctx.fillText(id, 10, y + this.rowHeight / 2); }); ctx.beginPath(); ctx.moveTo(0, graphBottomY); ctx.lineTo(width, graphBottomY); ctx.stroke(); const minPixelsPerLabel = APP_CONFIG.CANVAS_MIN_PIXELS_PER_LABEL; let step = 1; if (timePerPixel > 0 && timePerPixel < minPixelsPerLabel) { const rawStep = minPixelsPerLabel / timePerPixel; const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep))); const normalized = rawStep / magnitude; if (normalized <= 2) step = 2 * magnitude; else if (normalized <= 5) step = 5 * magnitude; else step = 10 * magnitude; } ctx.textAlign = 'center'; for (let t = 0; t <= totalTime; t += step) { const x = this.leftPanelWidth + t * timePerPixel; ctx.strokeStyle = '#cccccc'; ctx.beginPath(); ctx.moveTo(x, this.paddingTop); ctx.lineTo(x, graphBottomY); ctx.stroke(); ctx.fillStyle = '#006666'; ctx.fillText(t, x, graphBottomY + 15); } } drawExecutionBlocks(ctx, entry, rowIndex, timePerPixel) { const y = this.paddingTop + rowIndex * this.rowHeight + (this.rowHeight - this.blockHeight) / 2; const fillColor = entry.threadColor; for (let t = entry.startTime; t < entry.endTime; t++) { const x = this.leftPanelWidth + t * timePerPixel; ctx.fillStyle = fillColor; ctx.fillRect(x, y, timePerPixel, this.blockHeight); ctx.strokeStyle = '#000000'; ctx.lineWidth = 1; ctx.setLineDash([]); ctx.strokeRect(x, y, timePerPixel, this.blockHeight); } } }