GUI:n kirjoittaminen 1C RAC:lle tai taas Tcl/Tk:stä

Kun syventyimme aiheeseen 1C-tuotteiden toiminnasta Linux-ympäristössä, havaittiin yksi haittapuoli - kätevän graafisen monialustaisen työkalun puute 1C-palvelinklusterin hallintaan. Ja tämä epäkohta päätettiin korjata kirjoittamalla graafinen käyttöliittymä rac-konsoliapuohjelmaan. Kehityskieleksi valittiin Tcl/tk mielestäni sopivimmaksi tähän tehtävään. Ja siksi haluan esitellä joitakin mielenkiintoisia puolia ratkaisusta tässä materiaalissa.

Toimiaksesi tarvitset tcl/tk- ja 1C-jakaumia. Ja koska päätin ottaa kaiken irti tcl/tk-perustoimituksen ominaisuuksista ilman kolmannen osapuolen paketteja, tarvitsen version 8.6.7, joka sisältää ttk:n - paketin, jossa on lisägrafiikkaelementtejä, joista tarvitsemme pääasiassa ttk:tä. ::TreeView mahdollistaa tietojen näyttämisen sekä puurakenteen että taulukon (luettelon) muodossa. Myös uudessa versiossa työ poikkeuksin on muokattu (try-komento, jota käytetään projektissa ulkoisten komentojen ajettaessa).

Projekti koostuu useista tiedostoista (vaikka mikään ei estä sinua tekemästä kaikkea yhdessä):

rac_gui.cfg - oletusasetus
rac_gui.tcl - pääkäynnistysskripti
Lib-hakemisto sisältää tiedostoja, jotka ladataan automaattisesti käynnistyksen yhteydessä:
function.tcl - tiedosto menettelyillä
gui.tcl - tärkein graafinen käyttöliittymä
images.tcl - base64-kuvakirjasto

Itse asiassa rac_gui.tcl-tiedosto käynnistää tulkin, alustaa muuttujat, lataa moduuleja, määrityksiä ja niin edelleen. Tiedoston sisältö kommentteineen:

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"
    }    
}

Kun olet ladannut kaiken tarvittavan ja tarkistanut rac-apuohjelman olemassaolon, graafinen ikkuna avautuu. Ohjelman käyttöliittymä koostuu kolmesta osasta:

Työkalupalkki, puu ja luettelo

Tein ”puun” sisällön mahdollisimman samankaltaiseksi kuin 1C:n perus Windows-laitteet.

GUI:n kirjoittaminen 1C RAC:lle tai taas Tcl/Tk:stä

Tämän ikkunan muodostava pääkoodi on tiedostossa
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

Ohjelman kanssa työskentelyn algoritmi on seuraava:

1. Ensin sinun on lisättävä pääklusteripalvelin (eli klusterinhallintapalvelin (Linuxissa hallinta käynnistetään komennolla "/opt/1C/v8.3/x86_64/ras cluster —daemon").

Napsauta "+" -painiketta ja kirjoita avautuvaan ikkunaan palvelimen osoite ja portti:

GUI:n kirjoittaminen 1C RAC:lle tai taas Tcl/Tk:stä

Sen jälkeen palvelimemme ilmestyy puuhun napsauttamalla sitä, avautuu luettelo klustereista tai näyttöön tulee yhteysvirhe.

2. Napsauttamalla klusterin nimeä avautuu luettelo sille käytettävissä olevista toiminnoista.

3. ...

Ja niin edelleen, ts. lisätäksesi uuden klusterin, valitse mikä tahansa luettelosta saatavilla oleva klusteri ja paina "+" -painiketta työkalupalkissa, jolloin näkyviin tulee Lisää uusi -valintaikkuna:

GUI:n kirjoittaminen 1C RAC:lle tai taas Tcl/Tk:stä

Työkalupalkin painikkeet suorittavat toimintoja kontekstista riippuen, esim. Riippuen siitä, mikä puun tai luettelon elementti on valittu, suoritetaan yksi tai toinen toimenpide.

Katsotaanpa esimerkkiä lisäyspainikkeesta ("+"):

Painikkeen luontikoodi:

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

Tässä näemme, että kun painiketta painetaan, "Lisää" -toiminto suoritetaan, sen koodi:

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
}

Tässä on yksi ticklen eduista: voit antaa muuttujan arvon prosessin nimeksi:

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

Eli jos esimerkiksi osoitamme pääpalvelinta ja painamme "+", käynnistetään Add::server-menettely, jos klusterissa - Add::cluster ja niin edelleen (kirjoitan missä tarvittavat "avaimet" tulevat hieman alempana), luetellut menettelyt piirtävät kontekstiin sopivia graafisia elementtejä.

Kuten olet ehkä jo huomannut, lomakkeet ovat tyyliltään samanlaisia ​​- tämä ei ole yllättävää, koska ne näytetään yhdellä toimenpiteellä, tarkemmin sanottuna lomakkeen pääkehys (ikkuna, painikkeet, kuva, etiketti), toimenpiteen nimi LisääTopLevel

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
}

Kutsuparametrit: otsikko, kuvakkeen nimi kirjastosta (lib/images.tcl) ja valinnainen ikkunan nimiparametri (oletus.add). Siten, jos otamme yllä olevat esimerkit pääpalvelimen ja klusterin lisäämiseksi, puhelu on vastaavasti:

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

tai

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

No, jatkaen näillä esimerkeillä, näytän menettelyt, jotka näyttävät palvelimen tai klusterin lisäysikkunat.

Lisää palvelin

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
}

Lisää::klusteri

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
}

Kun verrataan näiden toimenpiteiden koodia, ero näkyy paljaalla silmällä; keskityn "Ok"-painikkeen käsittelijään. Tk:ssä graafisten elementtien ominaisuudet voidaan ohittaa ohjelman suorituksen aikana valinnalla määrittää. Esimerkiksi ensimmäinen komento painikkeen näyttämiseksi:

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

Mutta lomakkeissamme komento riippuu vaaditusta toiminnallisuudesta:

  .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
    }

Yllä olevassa esimerkissä "tukossa"-painike aloittaa klusterin lisäämisen.

Tässä kannattaa tehdä poikkeama Tk:n graafisten elementtien kanssa työskentelyyn - erilaisille tiedonsyöttöelementeille (syöttö, yhdistelmälaatikko, tarkistuspainike jne.) on lisätty parametri tekstimuuttujaksi:

entry  $frm.ent_lifetime_limit -textvariable lifetime_limit

Tämä muuttuja määritellään globaalissa nimiavaruudessa ja sisältää tällä hetkellä syötetyn arvon. Nuo. saadaksesi syötetyn tekstin kentästä, sinun tarvitsee vain lukea muuttujaa vastaava arvo (tietysti, jos se on määritetty elementtiä luotaessa).

Toinen tapa syötetyn tekstin hakemiseen (merkintätyyppisille elementeille) on käyttää get-komentoa:

.add.frm.ent_name get

Molemmat menetelmät näkyvät yllä olevassa koodissa.

Tässä tapauksessa tämän painikkeen napsauttaminen käynnistää RunCommand-proseduurin luodulla komentorivillä klusterin lisäämiseksi rac:n suhteen:

/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

Nyt päästään pääkomentoon, joka ohjaa rac:n käynnistämistä tarvitsemillamme parametreilla, jäsentää myös komentojen tulosteet listoiksi ja palauttaa tarvittaessa:

Suorita komento

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 ""
    }
}

Pääpalvelintietojen syöttämisen jälkeen ne lisätään puuhun, tätä varten yllä olevassa Add:server -menettelyssä seuraava koodi vastaa:

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

Napsauttamalla palvelimen nimeä puussa, saamme luettelon kyseisen palvelimen hallinnoimista klustereista ja klusteria napsauttamalla saamme luettelon klusterin elementeistä (palvelimet, tietokannat jne.). Tämä toteutetaan TreePress-menettelyssä (tiedosto lib/function.tcl):

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
}

Vastaavasti Run::server käynnistetään pääpalvelimelle (klusterille - Run::cluster, toimivalle palvelimelle - Run::work_server jne.). Nuo. $key-muuttujan arvo on osa valinnan määrittämän puuelementin nimeä -ID.

Kiinnitämme huomiota menettelyyn

Suorita::server

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
    }
}

Tämä proseduuri käsittelee palvelimelta RunCommand-komennon kautta vastaanotetun tiedon ja lisää puuhun kaikenlaisia ​​asioita - klustereita, erilaisia ​​juurielementtejä (kannat, toimivat palvelimet, istunnot ja niin edelleen). Jos katsot tarkasti, huomaat InsertItemsWorkList-menettelyn kutsun sisällä. Sitä käytetään elementtien lisäämiseen graafiseen luetteloon käsittelemällä rac-konsoliapuohjelman tulos, joka on aiemmin palautettu luettelona muuttujaan $lst. Tämä on luettelo luetteloista, jotka sisältävät kaksoispisteellä eroteltuja elementtipareja.

Esimerkiksi luettelo klusteriyhteyksistä:

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

Graafisessa muodossa se näyttää suunnilleen tältä:

GUI:n kirjoittaminen 1C RAC:lle tai taas Tcl/Tk:stä

Yllä oleva menettely valitsee otsikon elementtien nimet ja tiedot taulukon täyttämiseksi:

InsertItemsWorkList

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
}

Tässä yksinkertaisen komennon [split $str ":"] sijasta, joka jakaa merkkijonon elementeiksi, jotka erotetaan merkillä ":" ja palauttaa luettelon, käytetään säännöllistä lauseketta, koska jotkin elementit sisältävät myös kaksoispisteen.

InsertClusterItems-menettely (yksi useista samankaltaisista) yksinkertaisesti lisää luettelon lapsielementeistä ja vastaavista tunnisteista vaaditun klusterielementin puuhun
InsertClusterItems

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
}

Voit harkita kahta muuta vaihtoehtoa samanlaisen menettelyn toteuttamiseksi, joissa on selvästi nähtävissä, kuinka voit optimoida ja päästä eroon toistuvista komennoista:

Tässä menettelyssä lisääminen ja tarkistaminen ratkaistaan ​​suoraan:

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"
    }
}

Tässä on oikeampi lähestymistapa:

InsertProfileItems

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 
    }
}

Niiden välinen ero on silmukan käyttö, jossa toistetut komennot suoritetaan. Kehittäjä voi päättää, mitä lähestymistapaa käytetään.

Olemme käsitelleet elementtien lisäämistä ja tietojen hakemista, nyt on aika keskittyä muokkaamiseen. Koska muokkaamiseen ja lisäämiseen käytetään periaatteessa samoja parametreja (tietokantaa lukuun ottamatta), käytetään samoja dialogilomakkeita. Algoritmi lisäysmenettelyjen kutsumiseksi näyttää tältä:

Add::$key->AddToplevel

Ja muokkaukseen näin:

Edit::$key->Add::$key->AddTopLevel

Otetaan esimerkiksi klusterin muokkaaminen, ts. Kun olet napsauttanut klusterin nimeä puussa, paina työkalupalkin muokkauspainiketta (kynä) ja vastaava lomake tulee näkyviin näytölle:

GUI:n kirjoittaminen 1C RAC:lle tai taas Tcl/Tk:stä
Edit::klusteri

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
    }
}

Koodin kommenttien perusteella periaatteessa kaikki on selvää, paitsi että painikkeen käsittelijän koodi on ohitettu ja käytössä on FormFieldsDataInsert-proseduuri, joka täyttää kentät tiedoilla ja alustaa muuttujat:

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
                }
            }
        }
    }
}

Tässä menettelyssä nousi esiin toinen tcl:n etu - muiden muuttujien arvot korvataan muuttujien niminä. Nuo. Lomakkeiden täytön ja muuttujien alustuksen automatisoimiseksi kenttien ja muuttujien nimet vastaavat rac-apuohjelman komentorivikytkimiä ja komennon tulosparametrien nimiä eräällä poikkeuksella - viiva korvataan alaviivalla. Esim aikataulutetut työt-kielto vastaa kenttää ent_scheduled_jobs_deny ja muuttuva aikataulutetut_työt_kielto.

Lisäys- ja muokkauslomakkeet voivat vaihdella kenttien koostumuksessa, esimerkiksi tietokannan kanssa työskentelyssä:

Tietoturvan lisääminen

GUI:n kirjoittaminen 1C RAC:lle tai taas Tcl/Tk:stä

Tietoturvan muokkaaminen

GUI:n kirjoittaminen 1C RAC:lle tai taas Tcl/Tk:stä

Muokkaustoimenpiteessä Edit::infobase pakolliset kentät lisätään lomakkeeseen, koodi on runsas, joten en esitä sitä tässä.

Analogisesti lisäys-, muokkaus- ja poistamismenettelyt toteutetaan muille elementeille.

Koska apuohjelman toiminta edellyttää rajoittamatonta määrää palvelimia, klustereita, tietokantoja jne., jotta voidaan määrittää, mikä klusteri mihinkin palvelimeen tai tietoturvajärjestelmään kuuluu, on otettu käyttöön useita globaaleja muuttujia, joiden arvot asetetaan kullekin. kun napsautat puun elementtejä. Nuo. proseduuri kulkee rekursiivisesti kaikkien yläelementtien läpi ja asettaa muuttujat:

AsetaGlobalVarFromTreeItems

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
    }
}

1C-klusterin avulla voit työskennellä luvalla tai ilman. Järjestelmänvalvojia on kahdenlaisia: klusteriagentin järjestelmänvalvoja ja klusterin järjestelmänvalvoja. Vastaavasti oikean toiminnan varmistamiseksi otettiin käyttöön 4 muuta globaalia muuttujaa, jotka sisältävät järjestelmänvalvojan kirjautumistunnuksen ja salasanan. Nuo. Jos klusterissa on järjestelmänvalvojan tili, näkyviin tulee valintaikkuna, jossa voit syöttää kirjautumistunnuksesi ja salasanasi. Tiedot tallennetaan muistiin ja lisätään jokaiseen vastaavan klusterin komentoon.

Tämä on virheenkäsittelymenettelyn vastuulla.

ErrorParcing

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"
        }
    }
}

Nuo. riippuen siitä, mitä komento palauttaa, reaktio on vastaava.

Tällä hetkellä noin 95 prosenttia toiminnallisuuksista on toteutettu, jäljellä on vain työskennellä suojausprofiilien kanssa ja testata sitä =). Siinä kaikki. Pyydän anteeksi rypistynyttä tarinaa.

Koodi on perinteisesti saatavilla täällä.

Päivitys: Olen lopettanut suojausprofiilien käsittelyn. Nyt toiminnallisuus on 100 % toteutettu.

Päivitys 2: lokalisointi englanniksi ja venäjäksi lisätty, toimiminen win7:ssä on testattu
GUI:n kirjoittaminen 1C RAC:lle tai taas Tcl/Tk:stä

Lähde: will.com

Lisää kommentti