Ons skryf 'n GUI vir 1C RAC, of ​​weer oor Tcl / Tk

Terwyl ons in die onderwerp van hoe 1C-produkte in die Linux-omgewing werk, gedelf het, is een nadeel ontdek - die gebrek aan 'n gerieflike grafiese multiplatform-instrument vir die bestuur van 'n 1C-bedienerkluster. En daar is besluit om hierdie tekortkoming reg te stel deur 'n GUI vir die rac-konsole-nutsding te skryf. Die ontwikkelingstaal was tcl/tk as, na my mening, die geskikste vir hierdie taak. En nou wil ek 'n paar interessante aspekte van die oplossing in hierdie materiaal aanbied.

Om te werk, sal jy tcl / tk en 1C verspreidings nodig hê. En aangesien ek besluit het om die moontlikhede van die basiese aflewering van tcl / tk ten beste te benut sonder om derdeparty-pakkette te gebruik, benodig ons weergawe 8.6.7, wat ttk insluit - 'n pakket met bykomende grafiese elemente, waarvan ons hoofsaaklik ttk benodig :: TreeView, dit laat uitvoerdata toe in die vorm van 'n boomstruktuur en in die vorm van 'n tabel (lys). Die werk met uitsonderings is ook herontwerp in die nuwe weergawe (die probeer-opdrag, wat in die projek gebruik word wanneer eksterne opdragte uitgevoer word).

Die projek bestaan ​​uit verskeie lêers (hoewel niks verhoed dat alles een is nie):

rac_gui.cfg - verstek konfigurasie
rac_gui.tcl - hoofopstartskrif
Die lib-gids bevat lêers wat outomaties by opstart gelaai word:
function.tcl - lêer met prosedures
gui.tcl - hoof-GUI
images.tcl - base64 beeldbiblioteek

Die rac_gui.tcl-lêer begin in werklikheid die tolk, inisialiseer veranderlikes, laai modules, konfigurasies, ensovoorts. Die inhoud van die lêer met opmerkings:

rac_gui.tcl

#!/bin/sh
exec wish "$0" -- "$@"

# Устанавливаем текущий каталог
set dir(root) [pwd]
# Устанавливаем рабочий каталог, если его нет то создаём
set dir(work) [file join $env(HOME) .rac_gui]
if {[file exists $dir(work)] == 0 } {
    file mkdir $dir(work)    
}
# каталог с модулями
set dir(lib) "[file join $dir(root) lib]"

# загружаем пользовательский конфиг, если он отсутствует, то копируем дефолтный
if {[file exists [file join $dir(work) rac_gui.cfg]] ==0} {
    file copy [file join [pwd] rac_gui.cfg] [file join $dir(work) rac_gui.cfg]
} 
source [file join $dir(work) rac_gui.cfg]
# Код проверки наличия rac и правильности указания пути в конфиге
# если программа не найдена то будет выведен диалог для указания корректного пути
# и этот путь будет записан в пользовательский конфиг
if {[file exists $rac_cmd] == 0} {
    set rac_cmd [tk_getOpenFile -initialdir $env(HOME) -parent . -title "Укажите путь до rac" -initialfile rac]
    file copy [file join $dir(work) rac_gui.cfg] [file join $dir(work) rac_gui.cfg.bak] 
    set orig_file [open [file join $dir(work) rac_gui.cfg.bak] "r"]
    set file [open [file join $dir(work) rac_gui.cfg] "w"]
    while {[gets $orig_file line] >=0 } {
        if {[string match "set rac_cmd*" $line]} {
            puts $file "set rac_cmd $rac_cmd"
        } else {
            puts $file $line
        }
    }
    close $file
    close $orig_file
    #return "$host:$port"
    file delete [file join $dir(work) 1c_srv.cfg.bak] 
} else {
    puts "Found $rac_cmd"
}

set cluster_user ""
set cluster_pwd ""
set agent_user ""
set agent_pwd ""
## LOAD FILE ##
# Загружаем модули кроме gui.tcl так как его надо загрузить последним
foreach modFile [lsort [glob -nocomplain [file join $dir(lib) *.tcl]]] {
    if {[file tail $modFile] ne "gui.tcl"} {
        source $modFile
        puts "Loaded module $modFile"
    }
}
source [file join $dir(lib) gui.tcl]
source [file join $dir(work) rac_gui.cfg]

# Читаем файл со списком серверов 1С
# и добавляем в дерево
if [file exists [file join $dir(work) 1c_srv.cfg]] {
    set f [open [file join $dir(work) 1c_srv.cfg] "RDONLY"]
    while {[gets $f line] >=0} {
        .frm_tree.tree insert {} end -id "server::$line" -text "$line" -values "$line"
    }    
}

Nadat u alles afgelaai het wat nodig is en gekontroleer is vir die teenwoordigheid van die rac-hulpprogram, sal 'n grafiese venster geloods word. Die programkoppelvlak bestaan ​​uit drie elemente:

Werkbalk, boom en lys

Ek het die inhoud van die "boom" so soortgelyk as moontlik gemaak aan die gewone venstertoerusting van 1C.

Ons skryf 'n GUI vir 1C RAC, of ​​weer oor Tcl / Tk

Die hoofkode wat hierdie venster vorm, is in die lêer vervat
lib/gui.tcl

# установка размера и положения основного окна
# можно установить в переменную topLevelGeometry в конфиг программы
if {[info exists topLevelGeometry]} {
    wm geometry . $topLevelGeometry
} else {
    wm geometry . 1024x768
}
# Заголовок окна
wm title . "1C Rac GUI"
wm iconname . "1C Rac Gui"
# иконка окна (берется из файла lib/imges.tcl)
wm iconphoto . tcl
wm protocol . WM_DELETE_WINDOW Quit
wm overrideredirect . 0
wm positionfrom . user

ttk::style theme use clam

# Панель инсрументов
set frm_tool [frame .frm_tool]
pack $frm_tool -side left -fill y 
ttk::panedwindow .panel -orient horizontal -style TPanedwindow
pack .panel -expand true -fill both
pack propagate .panel false

ttk::button $frm_tool.btn_add  -command Add  -image add_grey_32
ttk::button $frm_tool.btn_del  -command Del -image del_grey_32
ttk::button $frm_tool.btn_edit  -command Edit -image edit_grey_32
ttk::button $frm_tool.btn_quit -command Quit -image quit_grey_32

pack $frm_tool.btn_add $frm_tool.btn_del $frm_tool.btn_edit -side top -padx 5 -pady 5
pack $frm_tool.btn_quit  -side bottom -padx 5 -pady 5

# Дерево с полосами прокрутки
set frm_tree [frame .frm_tree]

ttk::scrollbar $frm_tree.hsb1 -orient horizontal -command [list $frm_tree.tree xview]
ttk::scrollbar $frm_tree.vsb1 -orient vertical -command [list $frm_tree.tree yview]
set tree [ttk::treeview $frm_tree.tree -show tree 
-xscrollcommand [list $frm_tree.hsb1 set] -yscrollcommand [list $frm_tree.vsb1 set]]

grid $tree -row 0 -column 0 -sticky nsew
grid $frm_tree.vsb1 -row 0 -column 1 -sticky nsew
grid $frm_tree.hsb1 -row 1 -column 0 -sticky nsew
grid columnconfigure $frm_tree 0 -weight 1
grid rowconfigure $frm_tree 0 -weight 1

# назначение обработчика нажатия кнопкой мыши
bind $frm_tree.tree <ButtonRelease> "TreePress $frm_tree.tree"

# Список для данных (таблица)
set frm_work [frame .frm_work]
ttk::scrollbar $frm_work.hsb -orient horizontal -command [list $frm_work.tree_work xview]
ttk::scrollbar $frm_work.vsb -orient vertical -command [list $frm_work.tree_work yview]
set tree_work [
    ttk::treeview $frm_work.tree_work 
    -show headings  -columns "par val" -displaycolumns "par val"
    -xscrollcommand [list $frm_work.hsb set] 
    -yscrollcommand [list $frm_work.vsb set]
]
# Установка цветов для чередования в таблице
$tree_work tag configure dark -background $color(dark_table_bg)
$tree_work tag configure light -background $color(light_table_bg)

# Размещение элементов на форме
grid $tree_work -row 0 -column 0 -sticky nsew
grid $frm_work.vsb -row 0 -column 1 -sticky nsew
grid $frm_work.hsb -row 1 -column 0 -sticky nsew
grid columnconfigure $frm_work 0 -weight 1
grid rowconfigure $frm_work 0 -weight 1
pack $frm_tree $frm_work -side left -expand true -fill both

#.panel add $frm_tool -weight 1
.panel add $frm_tree -weight 1 
.panel add $frm_work -weight 1

Die algoritme om met die program te werk is soos volg:

1. Aan die begin moet jy die hoofklusterbediener byvoeg (d.w.s. die groepbestuurbediener (in Linux word bestuur begin deur die opdrag "/opt/1C/v8.3/x86_64/ras cluster -daemon")).

Om dit te doen, klik op die "+" knoppie en voer die bedieneradres en poort in die venster wat oopmaak in:

Ons skryf 'n GUI vir 1C RAC, of ​​weer oor Tcl / Tk

Daarna sal ons bediener in die boom verskyn deur daarop te klik, 'n lys van groepe sal oopmaak of 'n verbindingsfout sal vertoon word.

2. Deur op die groepnaam te klik, sal die lys van funksies wat daarvoor beskikbaar is, oopmaak.

3...

Wel, ensovoorts, d.w.s. Om 'n nuwe groep by te voeg, kies enige beskikbaar in die lys en druk die "+" knoppie in die nutsbalk, en die dialoog vir die byvoeging van 'n nuwe een sal vertoon word:

Ons skryf 'n GUI vir 1C RAC, of ​​weer oor Tcl / Tk

Knoppies in die nutsbalk verrig funksies na gelang van die konteks, m.a.w. afhangende van watter element van die boom of lys gekies is, sal hierdie of daardie prosedure uitgevoer word.

Beskou die voorbeeld van die voeg-knoppie ("+"):

Knoppie formasie kode:

ttk::button $frm_tool.btn_add  -command Add  -image add_grey_32

Hier sien ons dat wanneer die knoppie gedruk word, die "Voeg" prosedure uitgevoer sal word, die kode daarvan is:

proc Add {} {
    global active_cluster host
    # Определяем идентификатор выделенного элемента
    set id  [.frm_tree.tree selection] 
    # Определяем значение этого элемента
    set values [.frm_tree.tree item [.frm_tree.tree selection] -values]
    set key [lindex [split $id "::"] 0]
    # в зависимости от того что выделили будет запущена нужная процедура
    if {$key eq "" || $key eq "server"} {
        set host [ Add::server ]
        return
    }
    Add::$key .frm_tree.tree $host $values
}

Hier loer een van die pluspunte van die kielie deur - jy kan die waarde van 'n veranderlike as die naam van die prosedure deurgee:

Add::$key .frm_tree.tree $host $values

Dit is, byvoorbeeld, as ons by die hoofbediener insteek en "+" druk, dan sal die Add::server-prosedure geloods word, as in die cluster - Add::cluster, ensovoorts (ek sal 'n bietjie skryf oor waar die nodige "sleutels" van onder af kom), teken die gelyste prosedures grafiese elemente wat geskik is vir die konteks.

Soos u dalk opgemerk het, is die vorms soortgelyk in styl - dit is nie verbasend nie, want hulle word deur een prosedure vertoon, meer presies, die hoofraam van die vorm (venster, knoppies, beeld, etiket), die naam van die prosedure Voeg Topvlak by

proc AddToplevel {lbl img {win_name .add}} {
    set cmd "destroy $win_name"
    if [winfo exists $win_name] {destroy $win_name}
    toplevel $win_name
    wm title $win_name $lbl
    wm iconphoto $win_name tcl
    # метка с иконкой
    ttk::label $win_name.lbl -image $img
    # фрейм с полями ввода
    set frm [ttk::labelframe $win_name.frm -text $lbl -labelanchor nw]
    
    grid columnconfigure $frm 0 -weight 1
    grid rowconfigure $frm 0 -weight 1
    # фрейм и кнопки
    set frm_btn [frame $win_name.frm_btn -border 0]
    ttk::button $frm_btn.btn_ok -image ok_grey_24 -command { }
    ttk::button $frm_btn.btn_cancel -command $cmd -image quit_grey_24 
    grid $win_name.lbl -row 0 -column 0 -sticky nw -padx 5 -pady 10
    grid $frm -row 0 -column 1 -sticky nw -padx 5 -pady 5
    grid $frm_btn -row 1 -column 1 -sticky se -padx 5 -pady 5
    pack  $frm_btn.btn_cancel  -side right
    pack  $frm_btn.btn_ok  -side right -padx 10
    return $frm
}

Oproepparameters: titel, prentnaam vir die ikoon uit die biblioteek (lib/images.tcl) en opsionele vensternaamparameter (verstek .add). Dus, as ons die bogenoemde voorbeelde neem om die hoofbediener en groep by te voeg, sal die oproep onderskeidelik wees:

AddToplevel "Добавление основного сервера" server_grey_64

of

AddToplevel "Добавление кластера" cluster_grey_64

Wel, gaan voort met hierdie voorbeelde, ek sal die prosedures wys wat die byvoegingsdialoë vir 'n bediener of groepie vertoon.

Voeg by::bediener

proc Add::server {} {
    global default
    # выводим основную форму
    set frm [AddToplevel "Добавление основного сервера" server_grey_64]
    # добавляем етки и поля ввода на эту форму
    label $frm.lbl_host -text "Адрес сервера"
    entry  $frm.ent_host
    label $frm.lbl_port -text "Порт"
    entry $frm.ent_port 
    $frm.ent_port  insert end $default(port)
    grid $frm.lbl_host -row 0 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_host -row 0 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_port -row 1 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_port -row 1 -column 1 -sticky nsew -padx 5 -pady 5
    grid columnconfigure $frm 0 -weight 1
    grid rowconfigure $frm 0 -weight 1
    #set frm_btn [frame .add.frm_btn -border 0]
    # переопределяем обработчик нажатия кнопки
    .add.frm_btn.btn_ok configure -command {
        set host [SaveMainServer [.add.frm.ent_host get] [.add.frm.ent_port get]]
        .frm_tree.tree insert {} end -id "server::$host" -text "$host" -values "$host"
        destroy .add
        return $host
    }
    return $frm
}

Voeg by::cluster

proc Add::cluster {tree host values} {
    global default lifetime_limit expiration_timeout session_fault_tolerance_level
    global max_memory_size max_memory_time_limit errors_count_threshold security_level
    global load_balancing_mode kill_problem_processes 
    agent_user agent_pwd cluster_user cluster_pwd auth_agent
    if {$agent_user ne "" && $agent_pwd ne ""} {
        set auth_agent "--agent-user=$agent_user --agent-pwd=$agent_pwd"
    } else {
        set auth_agent ""
    }
    # устанавливаем глобальные переменные ()
    set lifetime_limit $default(lifetime_limit)
    set expiration_timeout $default(expiration_timeout)
    set session_fault_tolerance_level $default(session_fault_tolerance_level)
    set max_memory_size $default(max_memory_size)
    set max_memory_time_limit $default(max_memory_time_limit)
    set errors_count_threshold $default(errors_count_threshold)
    set security_level [lindex $default(security_level) 0]
    set load_balancing_mode [lindex $default(load_balancing_mode) 0]
    
    set frm [AddToplevel "Добавление кластера" cluster_grey_64]
    
    label $frm.lbl_host -text "Адрес основного сервера"
    entry  $frm.ent_host
    label $frm.lbl_port -text "Порт"
    entry $frm.ent_port 
    $frm.ent_port  insert end $default(port)
    label $frm.lbl_name -text "Название кластера"
    entry  $frm.ent_name
    label $frm.lbl_secure_connect -text "Защищённое соединение"
    ttk::combobox $frm.cb_security_level -textvariable security_level -values $default(security_level)
    label $frm.lbl_expiration_timeout -text "Останавливать выключенные процессы через:"
    entry  $frm.ent_expiration_timeout -textvariable expiration_timeout
    label $frm.lbl_session_fault_tolerance_level -text "Уровень отказоустойчивости"
    entry  $frm.ent_session_fault_tolerance_level -textvariable session_fault_tolerance_level
    label $frm.lbl_load_balancing_mode -text "Режим распределения нагрузки"
    ttk::combobox $frm.cb_load_balancing_mode -textvariable load_balancing_mode 
    -values $default(load_balancing_mode)
    label $frm.lbl_errors_count_threshold -text "Допустимое отклонение количества ошибок сервера, %"
    entry  $frm.ent_errors_count_threshold -textvariable errors_count_threshold
    label $frm.lbl_processes -text "Рабочие процессы:"
    label $frm.lbl_lifetime_limit -text "Период перезапуска, сек."
    entry  $frm.ent_lifetime_limit -textvariable lifetime_limit
    label $frm.lbl_max_memory_size -text "Допустимый объём памяти, КБ"
    entry  $frm.ent_max_memory_size -textvariable max_memory_size
    label $frm.lbl_max_memory_time_limit -text "Интервал превышения допустимого объёма памяти, сек."
    entry  $frm.ent_max_memory_time_limit -textvariable max_memory_time_limit
    label $frm.lbl_kill_problem_processes -justify left -anchor nw -text "Принудительно завершать проблемные процессы"
    checkbutton $frm.check_kill_problem_processes -variable kill_problem_processes -onvalue yes -offvalue no    
    
    grid $frm.lbl_host -row 0 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_host -row 0 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_port -row 1 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_port -row 1 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_name -row 2 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_name -row 2 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_secure_connect -row 3 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.cb_security_level -row 3 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_expiration_timeout -row 4 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_expiration_timeout -row 4 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_session_fault_tolerance_level -row 5 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_session_fault_tolerance_level -row 5 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_load_balancing_mode -row 6 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.cb_load_balancing_mode -row 6 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_errors_count_threshold -row 7 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_errors_count_threshold -row 7 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_processes -row 8 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.lbl_lifetime_limit -row 9 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_lifetime_limit -row 9 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_max_memory_size -row 10 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_max_memory_size -row 10 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_max_memory_time_limit -row 11 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_max_memory_time_limit -row 11 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_kill_problem_processes -row 12 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.check_kill_problem_processes -row 12 -column 1 -sticky nw -padx 5 -pady 5
    # переопределяем обработчик
    .add.frm_btn.btn_ok configure -command {
        RunCommand "" "cluster insert 
        --host=[.add.frm.ent_host get] 
        --port=[.add.frm.ent_port get] 
        --name=[.add.frm.ent_name get] 
        --expiration-timeout=$expiration_timeout 
        --lifetime-limit=$lifetime_limit 
        --max-memory-size=$max_memory_size 
        --max-memory-time-limit=$max_memory_time_limit 
        --security-level=$security_level 
        --session-fault-tolerance-level=$session_fault_tolerance_level 
        --load-balancing-mode=$load_balancing_mode 
        --errors-count-threshold=$errors_count_threshold 
        --kill-problem-processes=$kill_problem_processes 
        $auth_agent $host"
        Run::server $tree $host ""
        destroy .add
    }
    return $frm
}

Wanneer die kode van hierdie prosedures vergelyk word, is die verskil met die blote oog sigbaar, ek sal fokus op die OK knoppie hanteerder. In Tk kan die eienskappe van grafiese elemente tydens looptyd oorskryf word met die opsie instel. Byvoorbeeld, die aanvanklike knoppie-uitvoeropdrag:

ttk::button $frm_btn.btn_ok -image ok_grey_24 -command { }

Maar in ons vorms hang die opdrag af van die vereiste funksionaliteit:

  .add.frm_btn.btn_ok configure -command {
        RunCommand "" "cluster insert 
        --host=[.add.frm.ent_host get] 
        --port=[.add.frm.ent_port get] 
        --name=[.add.frm.ent_name get] 
        --expiration-timeout=$expiration_timeout 
        --lifetime-limit=$lifetime_limit 
        --max-memory-size=$max_memory_size 
        --max-memory-time-limit=$max_memory_time_limit 
        --security-level=$security_level 
        --session-fault-tolerance-level=$session_fault_tolerance_level 
        --load-balancing-mode=$load_balancing_mode 
        --errors-count-threshold=$errors_count_threshold 
        --kill-problem-processes=$kill_problem_processes 
        $auth_agent $host"
        Run::server $tree $host ""
        destroy .add
    }

In die voorbeeld hierbo begin die knoppie "geprop" die prosedure om 'n groep by te voeg.

Hier is dit die moeite werd om 'n afwyking te maak om met grafiese elemente in Tk te werk - vir verskeie data-invoerelemente (invoer, kombinasieboks, kontroleknoppie, ens.), word 'n parameter soos 'n teksveranderlike (teksveranderlike) bekendgestel:

entry  $frm.ent_lifetime_limit -textvariable lifetime_limit

Hierdie veranderlike word in die globale naamruimte gedefinieer en bevat die waarde wat tans ingevoer is. Dié. om die ingevoerde teks uit die veld te kry, moet u net die waarde lees wat ooreenstem met die veranderlike (natuurlik, mits dit gedefinieer is wanneer die element geskep is).

Die tweede metode om die ingevoerde teks te kry (vir elemente van tipe inskrywing) is om die get-opdrag te gebruik:

.add.frm.ent_name get

Albei hierdie metodes kan in die bogenoemde kode gesien word.

Deur hierdie knoppie te druk, in hierdie geval, begin die RunCommand-prosedure met die gegenereerde groep wat opdragstring byvoeg in terme van rac:

/opt/1C/v8.3/x86_64/rac cluster insert  --host=localhost  --port=1540  --name=dsdsds  --expiration-timeout=0  --lifetime-limit=0  --max-memory-size=0  --max-memory-time-limit=0  --security-level=0  --session-fault-tolerance-level=0  --load-balancing-mode=performance  --errors-count-threshold=0  --kill-problem-processes=no   localhost:1545

Ons het dus by die hoofopdrag gekom, wat die bekendstelling van rac beheer met die parameters wat ons nodig het, ook die uitvoer van opdragte in lyste ontleed en terugstuur, indien nodig:

RunCommand

proc RunCommand {root par} {
    global dir rac_cmd cluster work_list_row_count agent_user agent_pwd cluster_user cluster_pwd
    puts "$rac_cmd $par"
    set work_list_row_count 0
    # открываем канал в неблокирующем режиме
    # $rac - команда с полным путём
    # $par - сформированные ключи запуска и опции    
    set pipe [open "|$rac_cmd $par" "r"]
    try {
        set lst ""
        set l ""
        # вывод команды добавляем в список списков
        while {[gets $pipe line]>=0} {
            #puts $line
            if {$line eq ""} {
                lappend l $lst
                set lst ""
            } else {
                lappend lst [string trim $line]
            }
        }
        close $pipe
        return $l
    } on error {result options} {
        # Запуск обработчика ошибок
        ErrorParcing $result $options
        return ""
    }
}

Nadat die data van die hoofbediener ingevoer is, sal dit by die boom gevoeg word, hiervoor is die volgende kode in die Add:server-prosedure hierbo verantwoordelik:

.frm_tree.tree insert {} end -id "server::$host" -text "$host" -values "$host"

Nou, deur op die bedienernaam in die boom te klik, kry ons 'n lys van groepe wat deur hierdie bediener bestuur word, en deur op 'n groep te klik, kry ons 'n lys van groepelemente (bedieners, inligtingbasisse, ens.). Dit word geïmplementeer in die TreePress-prosedure (lib/function.tcl-lêer):

proc TreePress {tree} {
   global host server active_cluster infobase
   # определяем выделенный элемент
    set id  [$tree selection]
   # устанавливаем нужные глобальные переменные
    SetGlobalVarFromTreeItems $tree $id
   # Определяем ключ и значение, т.е. именно тип выбранного элемента
    set values [$tree item $id -values]
    set key [lindex [split $id "::"] 0]
   # и в зависимости от того что выбрали будет запущена соответствующая процедура 
   # в пространстве имён Run
    Run::$key $tree $host $values
}

Gevolglik sal Run::server vir die hoofbediener geloods word (Run::cluster vir die cluster, Run::work_server vir die werkende bediener, ens.). Dié. die waarde van die $key-veranderlike is deel van die naam van die boomelement wat deur die opsie gegee word -id.

Kom ons kyk na die prosedure

hardloop::bediener

proc Run::server {tree host values} {
    # получаем список кластеров требуемого сервера
    set lst [RunCommand server::$host "cluster list $host"]
    if {$lst eq ""} {return}
    set l [lindex $lst 0]
    #puts $lst
    # удаляем лишнее из списка
    .frm_work.tree_work delete  [ .frm_work.tree_work children {}]
    # читаем список
    foreach cluster_list $lst {
        # Заполняем список полученными значениями
        InsertItemsWorkList $cluster_list
        # обрабатываем вывод (список) для добавления данных в дерево
        foreach i $cluster_list {
            #puts $i
            set cluster_list [split $i ":"]
            if  {[string trim [lindex $cluster_list 0]] eq "cluster"} {
                set cluster_id [string trim [lindex $cluster_list 1]]
                lappend cluster($cluster_id) $cluster_id
            }
            if  {[string trim [lindex $cluster_list 0]] eq "name"} {
                lappend  cluster($cluster_id) [string trim [lindex $cluster_list 1]]
            }
        }
    }
    # добавляем кластеры в дерево
    foreach x [array names cluster] {
        set id [lindex $cluster($x) 0]
        if { [$tree exists "cluster::$id"] == 0 } {
            $tree insert "server::$host" end -id "cluster::$id" -text "[lindex $cluster($x) 1]" -values "$id"
            # добавляем элементы в кластер
            InsertClusterItems $tree $id
        }
    }
    if { [$tree exists "agent_admins::$id"] == 0 } {
        $tree insert "server::$host" end -id "agent_admins::$id" -text "Администраторы" -values "$id"
        #InsertClusterItems $tree $id
    }
}

Hierdie prosedure verwerk wat van die bediener ontvang is deur die RunCommand-opdrag, en voeg allerhande dinge by die boom – trosse, verskeie wortelelemente (basisse, werkende bedieners, sessies, ensovoorts). As jy mooi kyk, binnekant kan jy die oproep na die InsertItemsWorkList-prosedure sien. Dit word gebruik om items by 'n grafiese lys te voeg deur die afvoer van die rac-konsole-nutsding te verwerk wat voorheen as 'n lys in die $lst-veranderlike teruggestuur is. Dit is 'n lys lyste wat pare elemente bevat wat deur 'n dubbelpunt geskei is.

Byvoorbeeld, 'n lys van groepverbindings:

svk@svk ~]$ /opt/1C/v8.3/x86_64/rac connection list --cluster=783d2170-56c3-11e8-c586-fc75165efbb2 localhost:1545
connection     : dcf5991c-7d24-11e8-1690-fc75165efbb2
conn-id        : 0
host           : svk.home
process        : 79de2e16-56c3-11e8-c586-fc75165efbb2
infobase       : 00000000-0000-0000-0000-000000000000
application    : "JobScheduler"
connected-at   : 2018-07-01T14:49:51
session-number : 0
blocked-by-ls  : 0

connection     : b993293a-7d24-11e8-1690-fc75165efbb2
conn-id        : 0
host           : svk.home
process        : 79de2e16-56c3-11e8-c586-fc75165efbb2
infobase       : 00000000-0000-0000-0000-000000000000
application    : "JobScheduler"
connected-at   : 2018-07-01T14:48:52
session-number : 0
blocked-by-ls  : 0

Grafies sal dit so iets lyk:

Ons skryf 'n GUI vir 1C RAC, of ​​weer oor Tcl / Tk

Die bogenoemde prosedure onttrek die elementname vir die kopskrif en die data om die tabel te vul:

Voeg ItemsWorkList in

proc InsertItemsWorkList {lst} {
    global work_list_row_count
    # установка чередования цвета для строки
    if [expr $work_list_row_count % 2] {
        set tag dark
    } else {
        set tag light
    }
    # разбор строк на пары ключ - значение
    foreach i $lst {
        if [regexp -nocase -all -- {(D+)(s*?|)(:)(s*?|)(.*)} $i match param v2 v3 v4 value] {
            lappend column_list [string trim $param]
            lappend value_list [string trim $value]
        }
    }
     # заполнение таблицы
    .frm_work.tree_work configure -columns $column_list -displaycolumns $column_list
    .frm_work.tree_work insert {} end  -values $value_list -tags $tag
    .frm_work.tree_work column #0 -stretch
    # установка заголовков
    foreach j $column_list {
        .frm_work.tree_work heading $j -text $j
    }
    incr work_list_row_count
}

Hier, in plaas van 'n eenvoudige opdrag [split $str ":"], wat die string verdeel in elemente geskei deur ":" en 'n lys gee, word 'n gewone uitdrukking gebruik, aangesien sommige elemente ook 'n dubbelpunt bevat.

Die InsertClusterItems-prosedure (een van verskeie soortgelyke) voeg eenvoudig 'n lys van kinderelemente met die ooreenstemmende identifiseerders by die vereiste groepelement in die boom
Voeg ClusterItems in

proc InsertClusterItems {tree id} {
    set parent "cluster::$id"
    $tree insert $parent end -id "infobases::$id" -text "Информационные базы" -values "$id"
    $tree insert $parent end -id "servers::$id" -text "Рабочие серверы" -values "$id"
    $tree insert $parent end -id "admins::$id" -text "Администраторы" -values "$id"
    $tree insert $parent end -id "managers::$id" -text "Менеджеры кластера" -values $id
    $tree insert $parent end -id "processes::$id" -text "Рабочие процессы" -values "workprocess-all"
    $tree insert $parent end -id "sessions::$id" -text "Сеансы" -values "sessions-all"
    $tree insert $parent end -id "locks::$id" -text "Блокировки" -values "blocks-all"
    $tree insert $parent end -id "connections::$id" -text "Соединения" -values "connections-all"
    $tree insert $parent end -id "profiles::$id" -text "Профили безопасности" -values $id
}

U kan nog twee opsies oorweeg om so 'n prosedure te implementeer, waar dit duidelik gesien sal word hoe u herhalende opdragte kan optimaliseer en ontslae kan raak:

In hierdie prosedure word byvoeging en kontrolering van voor af opgelos:

InsertBaseItems

proc InsertBaseItems {tree id} {
    set parent "infobase::$id"
    if { [$tree exists "sessions::$id"] == 0 } {
        $tree insert $parent end -id "sessions::$id" -text "Сеансы" -values "$id"
    }
    if { [$tree exists "locks::$id"] == 0 } {
        $tree insert $parent end -id "locks::$id" -text "Блокировки" -values "$id"
    }
    if { [$tree exists "connections::$id"] == 0 } {
        $tree insert $parent end -id "connections::$id" -text "Соединения" -values "$id"
    }
}

En hier is 'n beter benadering:

Voeg profielitems in

proc InsertProfileItems {tree id} {
    set parent "profile::$id"
    set lst {
        {dir "Виртуальные каталоги"}
        {com "Разрешённые COM-классы"}
        {addin "Внешние компоненты"}
        {module "Внешние отчёты и обработки"}
        {app "Разрешённые приложения"}
        {inet "Ресурсы интернет"}
    }
    foreach i $lst {
        append item [lindex $i 0] "::$id"
        if { [$tree exists $item] == 0 } {
            $tree insert $parent end -id $item -text [lindex $i 1] -values "$id"
        }
        unset item 
    }
}

Die verskil tussen hulle is die toepassing van die siklus, waarin die herhaalde opdrag(te) uitgevoer word. Watter benadering om te gebruik, is volgens die diskresie van die ontwikkelaar.

Om elemente by te voeg en data te kry, het ons oorweeg, dit is tyd om op te hou met redigering. Aangesien dieselfde parameters basies gebruik word vir redigering en byvoeging (die uitsondering is die infobasis), word die dialoogvorms dieselfde gebruik. Die algoritme vir die oproep van prosedures om by te voeg, lyk soos volg:

Voeg by::$key->AddTopLevel

En vir redigering soos hierdie:

Wysig::$key->Add::$key->AddTopLevel

Kom ons neem byvoorbeeld cluster redigering, d.w.s. deur in die boom op die naam van die groepie te klik, druk die wysig-knoppie in die nutsbalk (potlood) en die ooreenstemmende vorm sal op die skerm vertoon word:

Ons skryf 'n GUI vir 1C RAC, of ​​weer oor Tcl / Tk
Edit::cluster

proc Edit::cluster {tree host values} {
    global default lifetime_limit expiration_timeout session_fault_tolerance_level
    global max_memory_size max_memory_time_limit errors_count_threshold security_level
    global load_balancing_mode kill_problem_processes active_cluster 
    agent_user agent_pwd cluster_user cluster_pwd auth
    if {$cluster_user ne "" && $cluster_pwd ne ""} {
        set auth "--cluster-user=$cluster_user --cluster-pwd=$cluster_pwd"
    } else {
        set auth ""
    }
    # рисуем форму для кластера
    set frm [Add::cluster $tree $host $values]
    # меняем текст на метке
    $frm configure -text "Редактирование кластера"
    
    set active_cluster $values
    # получаем данные по выделенному кластеру
    set lst [RunCommand cluster::$values "cluster info --cluster=$active_cluster $host"]
    # заполняем поля
    FormFieldsDataInsert $frm $lst
    # выключаем поля, редактирование которых запрещено
    $frm.ent_host configure -state disable
    $frm.ent_port configure -state disable
    # переназначаем обработчик
    .add.frm_btn.btn_ok configure -command {
        RunCommand "" "cluster update 
        --cluster=$active_cluster $auth 
        --name=[.add.frm.ent_name get] 
        --expiration-timeout=$expiration_timeout 
        --lifetime-limit=$lifetime_limit 
        --max-memory-size=$max_memory_size 
        --max-memory-time-limit=$max_memory_time_limit 
        --security-level=$security_level 
        --session-fault-tolerance-level=$session_fault_tolerance_level 
        --load-balancing-mode=$load_balancing_mode 
        --errors-count-threshold=$errors_count_threshold 
        --kill-problem-processes=$kill_problem_processes 
        $auth $host"
        $tree delete "cluster::$active_cluster"
        Run::server $tree $host ""
        destroy .add
    }
}

Volgens die opmerkings in die kode is alles in beginsel duidelik, behalwe dat die knoppie-hanteerderkode herdefinieer word en daar 'n FormFieldsDataInsert-prosedure is wat die velde met data vul en die veranderlikes inisialiseer:

FormFieldsDataInsert

proc FormFieldsDataInsert {frm lst} {
    foreach i [lindex $lst 0] {
        # получаем список параметров и значений
        if [regexp -nocase -all -- {(D+)(s*?|)(:)(s*?|)(.*)} $i match param v2 v3 v4 value] {
            # меняем символы
            regsub -all -- "-" [string trim $param] "_" entry_name
            # заполняем данными
            if [winfo exists $frm.ent_$entry_name] {
                $frm.ent_$entry_name delete 0 end
                $frm.ent_$entry_name insert end [string trim $value """]
            }
            if [winfo exists $frm.cb_$entry_name] {
                global $entry_name
                set $entry_name [string trim $value """]
            }
            # для чекбоксов меняем значения
            if [winfo exists $frm.check_$entry_name] {
                global $entry_name
                if {$value eq "0"} {
                    set $entry_name no
                } elseif {$value eq "1"} {
                    set $entry_name yes
                } else {
                    set $entry_name $value
                }
            }
        }
    }
}

In hierdie prosedure het nog 'n pluspunt van tcl opgeduik - die waardes van ander veranderlikes word as veranderlike name vervang. Dié. om die invul van vorms en die inisialisasie van veranderlikes te outomatiseer, stem die name van velde en veranderlikes ooreen met die opdraglynskakelaars van die rac-nutsprogram en die name van opdraguitvoerparameters met 'n paar uitsondering - die streep is met 'n onderstreep vervang. Bv geskeduleerde-werk-weier ooreenstem met die veld ent_geskeduleerde_jobs_weier en veranderlik geskeduleerde_werksgeleenthede_weier.

Vorms vir byvoeging en wysiging kan verskil in die samestelling van die velde, byvoorbeeld om met 'n inligtingsbasis te werk:

Voeg IB by

Ons skryf 'n GUI vir 1C RAC, of ​​weer oor Tcl / Tk

IB redigering

Ons skryf 'n GUI vir 1C RAC, of ​​weer oor Tcl / Tk

In die redigeerprosedure Edit::infobase word die vereiste velde by die vorm gevoeg, so ek gee nie die kode hier nie.

Na analogie word prosedures vir byvoeging, redigering, verwydering vir ander elemente geïmplementeer.

Aangesien die werking van die nutsprogram 'n onbeperkte aantal bedieners, groepe, inligtingsbasisse, ens. impliseer om te bepaal aan watter groepering 'n bediener of IS behoort, word verskeie globale veranderlikes bekendgestel, waarvan die waardes gestel word elke keer as jy op klik die elemente van die boom. Dié. die prosedure loop rekursief deur alle ouerelemente en stel veranderlikes:

StelGlobalVarFromTreeItems

proc SetGlobalVarFromTreeItems {tree id} {
    global host server active_cluster infobase
    set parent [$tree parent $id]
    set values [$tree item $id -values]
    set key [lindex [split $id "::"] 0]
    switch -- $key {
        server {set host $values}
        work_server {set server $values}
        cluster {set active_cluster $values}
        infobase {set infobase $values}
    }
    if {$parent eq ""} {
        return
    } else {
        SetGlobalVarFromTreeItems $tree $parent
    }
}

Cluster 1C laat jou toe om met of sonder magtiging te werk. Daar is twee soorte administrateurs - cluster agent administrateur en cluster administrateur. Gevolglik, vir korrekte werking, is nog 4 globale veranderlikes bekendgestel, wat die administrateur se login en wagwoord bevat. Dié. as daar 'n administrateurrekening in die groep is, sal 'n dialoog vertoon word om 'n login en wagwoord in te voer, die data sal in die geheue gestoor word en in elke opdrag vir die ooreenstemmende groepering vervang word.

Dit is die verantwoordelikheid van die fouthanteringsprosedure.

Fout Parsering

proc ErrorParcing {err opt} {
    global cluster_user cluster_pwd agent_user agent_pwd
        switch -regexp -- $err {
        "Cluster administrator is not authenticated" {
            AuthorisationDialog "Администратор кластера"
            .auth_win.frm_btn.btn_ok configure -command {
                set cluster_user [.auth_win.frm.ent_name get]
                set cluster_pwd [.auth_win.frm.ent_pwd get]
                destroy .auth_win
            }
            #RunCommand $root $par
        }
        "Central server administrator is not authenticated" {
            AuthorisationDialog "Администратор агента кластера"
            .auth_win.frm_btn.btn_ok configure -command {
                set agent_user [.auth_win.frm.ent_name get]
                set agent_pwd [.auth_win.frm.ent_pwd get]
                destroy .auth_win
            }
        }
        "Администратор кластера не аутентифицирован" {
            AuthorisationDialog "Администратор кластера"
            .auth_win.frm_btn.btn_ok configure -command {
                set cluster_user [.auth_win.frm.ent_name get]
                set cluster_pwd [.auth_win.frm.ent_pwd get]
                destroy .auth_win
            }
            #RunCommand $root $par
        }
        "Администратор центрального сервера не аутентифицирован" {
            AuthorisationDialog "Администратор агента кластера"
            .auth_win.frm_btn.btn_ok configure -command {
                set agent_user [.auth_win.frm.ent_name get]
                set agent_pwd [.auth_win.frm.ent_pwd get]
                destroy .auth_win
            }
        }
        (.+) {
            tk_messageBox -type ok -icon error -message "$err"
        }
    }
}

Dié. afhangende van wat die opdrag gee, sal daar 'n reaksie dienooreenkomstig wees.

Op die oomblik word die funksionaliteit met ongeveer 95 persent geïmplementeer, dit bly om werk met sekuriteitsprofiele te implementeer en dit te toets =). Dis al. Ek vra om verskoning vir die opgefrommelde storie.

Kode, tradisioneel beskikbaar hier.

Opdatering: Klaar werk met sekuriteitsprofiele. Nou is die funksionaliteit 100% geïmplementeer.

Opdatering 2: bygevoeg lokalisering in Engels en Russies, getoets werk in win7
Ons skryf 'n GUI vir 1C RAC, of ​​weer oor Tcl / Tk

Bron: will.com

Voeg 'n opmerking