Как создать текстовый интерфейс (TUI / TLI) для мониторинга файла журнала решателя OpenFOAM? - PullRequest
0 голосов
/ 29 августа 2018

Я запускаю симуляции OpenFOAM на кластере, и на их завершение уходят дни. Я ищу способ контролировать процесс и получить осмысленное представление. На данный момент я могу наблюдать за хвостом файла журнала, используя

watch tail -n 15 log.log

С здесь Я также нашел хороший скрипт GnuPlot-grep:

set logscale y
set title "Residuals"
set ylabel 'Residual'
set xlabel 'Iteration'
plot "< cat log.log | grep 'Solving for Ux'    | cut -d' ' -f9 | tr -d ','" title 'Ux'                  with lines,\
     "< cat log.log | grep 'Solving for Uy'    | cut -d' ' -f9 | tr -d ','" title 'Uy'                  with lines,\
     "< cat log.log | grep 'Solving for Uz'    | cut -d' ' -f9 | tr -d ','" title 'Uz'                  with lines,\
     "< cat log.log | grep 'Solving for omega' | cut -d' ' -f9 | tr -d ','" title 'omega'               with lines,\
     "< cat log.log | grep 'Solving for k'     | cut -d' ' -f9 | tr -d ','" title 'k'                   with lines,\
     "< cat log.log | grep 'Solving for p'     | cut -d' ' -f9 | tr -d ','" title 'p'                   with lines,\
     "< cat log.log | grep 'Courant Number'    | cut -d' ' -f9 | tr -d ','" title 'Courant Number mean' with lines,\
     "< cat log.log | grep 'Courant Number'    | cut -d' ' -f6 | tr -d ','" title 'Courant Number max'  with lines
pause 1
reread

, который извлекает информацию из файла log.log, и если я добавлю set term dumb где-нибудь сверху, он может отобразиться в терминале. Тем не менее, сюжет очень переполнен, он уродлив, показывать его нужно вечно, и он печатает на терминал последовательно, а не обновляет прежний.

Поиск в Интернете Я вижу, что есть несколько хороших библиотек Python, таких как npyscreen / picotui, ncurses / blessed, Asciimatics, Urwid, Prompt Toolkit ... для создания TUI / TLI. Мне было интересно, не могли бы вы помочь мне узнать, как я могу создать текстовый интерфейс для отображения базовой информации и графика выбранных значений в зависимости от времени. Я хочу иметь пару панелей. Один, чтобы выбрать переменную, которую я хочу построить, например, Courant Number mean, а на другой панели есть график, показывающий эту переменную в зависимости от времени шага. и другие, чтобы показать последние значения всех переменных в режиме реального времени. То, что я имею в виду, должно напоминать пример urwind graph.py :

enter image description here

P.S. Поскольку я разместил это сообщение:

  • Здесь Я познакомился с Termgraph очень интересной библиотекой python, чтобы получить некоторые графики в терминале.
  • Я отправил эту идею в группу Google Urwid. Вы можете следить за обсуждением здесь .
  • Я узнал о PyFoam CaseBuilder, который также использует Urwid. Также здесь Мне сообщили о других попытках в рамках проекта PyFoam получить некоторую полезную информацию TUI от решателя.

1 Ответ

0 голосов
/ 03 сентября 2018

Как описано в комментариях выше, я сделал вам пример кода. Он основан на Redis , и я предлагаю вам запустить Redis на вашем узле менеджера кластера, который, предположительно, находится близко к узлам вашего кластера и всегда работает - так что хороший кандидат на статистику Служба сбора.

Пример кода - это фиктивное задание, написанное на Python , и подпрограмма мониторинга, написанная на bash, но задание можно так же легко написать на C / C ++, а подпрограмму мониторинга - на Perl - есть все виды привязок для Redis - не зацикливайтесь на языке.

Даже если вы не умеете читать Python , это очень легко понять. Есть 3 потока, которые работают параллельно. Один просто обновляет string в Redis с общим истекшим временем обработки. Два других обновления Redis lists с данными временных рядов - синтезированной треугольной волны - одна работает на частоте 5 Гц, а другая - на частоте 1 Гц.

Я использовал строку Redis , где переменные не должны записывать историю, и список Redis , где нужна история. Другие структуры данных доступны.

В приведенном ниже коде только 3 интересные строки:

# Connect to Redis server by IP address/name
r = redis.Redis(host='localhost', port=6379, db=0)

# Set a Redis string called 'processTime' to value `processsTime`
r.set('processTime', processTime)

# Push a value to left end of Redis list
r.lpush(RedisKeyName, value)

Вот фиктивная работа, за которой следят. Начните читать там, где написано

######
# Main
######

Вот код:

#!/usr/local/bin/python3

import redis
import _thread
import time
import os
import random

################################################################################
# Separate thread periodically updating the 'processTime' in Redis
################################################################################
def processTimeThread():
   """Calculate time since we started and update every so often in Redis"""
   start = time.time()
   while True:
      processTime = int(time.time() - start)
      r.set('processTime', processTime)
      time.sleep(0.2)

################################################################################
# Separate thread generating a times series and storing in Redis with the given
# name and update rate
################################################################################
def generateSeriesThread(RedisKeyName, interval):
   """Generate a saw-tooth time series and log to Redis"""
   # Delete any values from previous runs
   r.delete(RedisKeyName)
   value = 0
   inc = 1
   while True:
      # Generate next value and store in Redis
      value = value + inc
      r.lpush(RedisKeyName, value)
      if value == 0:
         inc = 1
      if value == 10:
         inc = -1
      time.sleep(interval)

################################################################################
# Main
################################################################################

# Connect to Redis on local host - but could just as easily be on another machine
r = redis.Redis(host='localhost', port=6379, db=0)

# Get start time of job in RFC2822 format
startTime=time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
# ... and set Redis string "startTime"
r.set('startTime',startTime)

# Get process id (pid)
pid=os.getpid()
# ... and set Redis string "pid""
r.set('pid',pid)

# Start some threads generating data
_thread.start_new_thread( processTimeThread, () )
_thread.start_new_thread( generateSeriesThread, ('seriesA', 0.2) )
_thread.start_new_thread( generateSeriesThread, ('seriesB', 1) )

# Hang around (with threads still running) till user presses a key
key = input("Press Return/Enter to stop.")

Затем я написал сценарий мониторинга в bash, который подключается к Redis, захватывает значения и отображает их на терминале в TUI (текстовом интерфейсе пользователя). Вы можете в равной степени использовать Python или Perl или PHP и в равной степени написать графический интерфейс или веб-интерфейс.

#!/bin/bash

################################################################################
# drawGraph
################################################################################
drawGraph(){
   top=$1 ; shift
   data=( "$@" )
   for ((row=0;row<10;row++)) ; do
      ((y=10-row))
      ((screeny=top+row))
      line=""
      for ((col=0;col<30;col++)) ; do
         char=" "
         declare -i v
         v=${data[col]}
         [ $v -eq $y ] && char="X"
         line="${line}${char}"
      done
      printf "$(tput cup $screeny 0)%s" "${line}"
   done
}

# Save screen and clear and make cursor invisible
tput smcup
tput clear
tput civis

# Trap exit
trap 'exit 1' INT TERM
trap 'tput rmcup; tput clear' EXIT

while :; do
   # Get processid from Redis and display
   pid=$(redis-cli <<< "get pid")
   printf "$(tput cup 0 0)ProcessId: $pid"

   # Get process start time from Redis and display
   startTime=$(redis-cli <<< "get startTime")
   printf "$(tput cup 1 0)Start Time: $startTime"

   # Get process running time from Redis and display
   processTime=$(redis-cli <<< "get processTime")
   printf "$(tput cup 2 0)Running Time: $(tput el)$processTime"

   # Display seriesA last few values
   seriesA=( $(redis-cli <<< "lrange seriesA 0 30") )
   printf "$(tput cup 5 0)seriesA latest values: $(tput el)"
   printf "%d " "${seriesA[@]}"

   # Display seriesB last few values
   seriesB=( $(redis-cli <<< "lrange seriesB 0 30") )
   printf "$(tput cup 6 0)seriesB latest values: $(tput el)"
   printf "%d " "${seriesB[@]}"

   drawGraph 8  "${seriesA[@]}"
   drawGraph 19 "${seriesB[@]}"

   # Put cursor at bottom of screen and tell user how to quit
   printf "$(tput cup 30 0)Hit Ctrl-C to quit"
done

Надеюсь, вы увидите, что вы можете очень легко получить структуры данных из Redis. Получается переменная processTime, установленная в задании на узле кластера:

processTime=$(redis-cli <<< "get processTime")

TUI выглядит так:

enter image description here

...