Kirjutame GUI 1C RAC jaoks või taas Tcl/Tk-st

Sügisel 1C toodete töö uurimise käigus Linuxi keskkonnas ilmnes üks puudus - mugava graafilise platvormidevahelise tööriista puudumine 1C serveriklastri haldamiseks. See puudus otsustati lahendada GUI kirjutamisega rac käsurea tööriista jaoks. Arendamiseks valiti tcl/tk, kuna see tundus mulle selle ülesande jaoks kõige sobivam. Ja siin on mõned huvitavad aspektid, mida soovin selles materjalis esitada.

Tööks on vajalikud tcl/tk ja 1C distributsioonid. Kuna otsustasin maksimaalselt kasutada tcl/tk põhiversiooni võimalusi ilma kolmandate osapoolte pakette kasutamata, on vajalik versioon 8.6.7, mis sisaldab ttk-d - paketti, mis sisaldab lisagraafilisi elemente, millest vajame peamiselt ttk::TreeView-d, mis võimaldab andmeid kuvada nii puustruktuuri kui ka tabeli (loendi) kujul. Uues versioonis on ka parandatud töötlust puudutavate eranditega (kommando try, mida projektis kasutatakse väliste käskude käivitamisel).

Projekt koosneb mitmest failist (kuigi ei ole midagi takistamas kõike ühte teha):

rac_gui.cfg - vaikimisi konfiguratsioon
rac_gui.tcl - põhiskript käivitamiseks
Kaustas lib asuvad failid, mis laaditakse automaatselt käivitamisel:
function.tcl - fail protseduuride jaoks
gui.tcl - põhigraafiline liides
images.tcl - pildiraamat base64 formaadis

Fail rac_gui.tcl käivitab tõlgendi, initsialiseerib muutujad, laadib moodulid, konfiguratsioonid jne. Faili sisu kommentaaridega:

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

Pärast kõigi vajalike elementide laadimist ja rac tööriista olemasolu kontrollimist avatakse graafiline aken. Programmi liides koosneb kolmest elemendist:

Tööriistariba, puu ja loend

Puustruktuuri sisu on tehtud võimalikult sarnaseks 1C vaikimisi Windowsi komponendiga.

Kirjutame GUI 1C RAC jaoks või taas Tcl/Tk-st

Peamine kood, mis kujundab selle akna, asub failis
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

Programmi tööprotsess on järgmine:

1. Alguses tuleb lisada klastrite põhi server (st klastri haldussüsteem (Linuxis haldamine käivitub käsuga "opt/1C/v8.3/x86_64/ras cluster --daemon").

Selle jaoks peate vajutama nuppu „+“ ja avanevas aknas sisestama serveri aadressi ja pordi:

Kirjutame GUI 1C RAC jaoks või taas Tcl/Tk-st

Pärast seda ilmub puusse meie server, millel klõpsates avaneb klastrite loend või kuvatakse ühenduse viga.

2. Klõpsates klusteri nimele, avatakse saadaval olevate funktsioonide loend.

3.…

Ja nii edasi, st et uue klustiga lisamiseks valime loendist mis tahes saadaval oleva ja vajutame tööriistariba nuppu „+“, pärast mida avatakse uue lisamise dialoog.

Kirjutame GUI 1C RAC jaoks või taas Tcl/Tk-st

Tööriistariba nupud täidavad sõltuvalt kontekstist funktsioone, st sõltuvalt sellest, milline puu või loendi element on valitud, käivitatakse konkreetne protseduur.

Vaatame lisamise nupu („+“) näidet:

Nupu loomise kood:

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

Siit näeme, et nupule vajutades käivitatakse protseduur „Add“, mille kood on:

proc Add {} {
    global active_cluster host
    # Määrame valitud elemendi identifikaatori
    set id  [.frm_tree.tree selection] 
    # Määrame selle elemendi väärtuse
    set values [.frm_tree.tree item [.frm_tree.tree selection] -values]
    set key [lindex [split $id "::"] 0]
    # sõltuvalt sellest, mida valiti, käivitatakse vajalik protseduur
    if {$key eq "" || $key eq "server"} {
        set host [ Add::server ]
        return
    }
    Add::$key .frm_tree.tree $host $values
}

Siin hakkab silma ühe tikli pluss – protseduuri nimeks võib anda muutuja väärtuse:

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

St, näiteks kui me klõpsame peamise serveri peal ja vajutame „+“, siis käivitatakse protseduur Add::server, kui klusterile – Add::cluster ja nii edasi (kust need vajalikud „võtmed“ tulevad, selgitan natuke hiljem), loetletud protseduurid joonistavad graafilised elemendid, mis vastavad kontekstile.

Nagu olete juba võib-olla märganud, on vormid stiililt sarnased – see ei ole üllatav, kuna neid kuvab üks protseduur, täpsemalt vormi põhiraam (aken, nupud, pilt, silt), protseduuri nimi. AddTopLevel

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
    # silt koos ikooniga
    ttk::label $win_name.lbl -image $img
    # raam sisestusväljade jaoks
    set frm [ttk::labelframe $win_name.frm -text $lbl -labelanchor nw]
    
    grid columnconfigure $frm 0 -weight 1
    grid rowconfigure $frm 0 -weight 1
    # raam ja nupud
    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
}

Kutsungivõimet: pealkiri, pildi nimi ikooni jaoks raamatukogust (lib/images.tcl) ja valikuline akna nimi (vaikimisi .add). Seega, kui võtta ülaltoodud näited peaserveri ja klastrite lisamiseks, siis kutse vastavalt:

AddToplevel "Peaserveri lisamine" server_grey_64

või

AddToplevel "Klastri lisamine" cluster_grey_64

Ja jätkates nende näidetega, näitan protseduure, mis kuvavad dialooge serveri või klastrite lisamiseks.

Add::server

proc Add::server {} {
    global default
    # kuvame põhivormi
    set frm [AddToplevel "Peaserveri lisamine" server_grey_64]
    # lisame etikette ja sisestusvälju sellele vormile
    label $frm.lbl_host -text "Serveri aadress"
    entry  $frm.ent_host
    label $frm.lbl_port -text "Port"
    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]
    # ületame nupu vajutamise käsitleja
    .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
}

Add::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 ""
    }
    # määrame globaalmuutujad ()
    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 "Klaster lisamine" cluster_grey_64]
    
    label $frm.lbl_host -text "Peaserveri aadress"
    entry  $frm.ent_host
    label $frm.lbl_port -text "Port"
    entry $frm.ent_port 
    $frm.ent_port  insert end $default(port)
    label $frm.lbl_name -text "Klastri nimetus"
    entry  $frm.ent_name
    label $frm.lbl_secure_connect -text "Turvaline ühendus"
    ttk::combobox $frm.cb_security_level -textvariable security_level -values $default(security_level)
    label $frm.lbl_expiration_timeout -text "Peatu mitteaktiveeritud protsessid pärast:"
    entry  $frm.ent_expiration_timeout -textvariable expiration_timeout
    label $frm.lbl_session_fault_tolerance_level -text "Vea taluvuse tase"
    entry  $frm.ent_session_fault_tolerance_level -textvariable session_fault_tolerance_level
    label $frm.lbl_load_balancing_mode -text "Koormuse jaotamise režiim"
    ttk::combobox $frm.cb_load_balancing_mode -textvariable load_balancing_mode 
    -values $default(load_balancing_mode)
    label $frm.lbl_errors_count_threshold -text "Lubatud serveri vigade arv, %"
    entry  $frm.ent_errors_count_threshold -textvariable errors_count_threshold
    label $frm.lbl_processes -text "Tööprotsessid:"
    label $frm.lbl_lifetime_limit -text "Taaskäivitamise aeg, sek."
    entry  $frm.ent_lifetime_limit -textvariable lifetime_limit
    label $frm.lbl_max_memory_size -text "Lubatud mälu maht, KB"
    entry  $frm.ent_max_memory_size -textvariable max_memory_size
    label $frm.lbl_max_memory_time_limit -text "Lubatud mälu ületamise periood, sek."
    entry  $frm.ent_max_memory_time_limit -textvariable max_memory_time_limit
    label $frm.lbl_kill_problem_processes -justify left -anchor nw -text "Sundida probleemsete protsesside lõpetamine"
    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
    # kohandame töötluse
    .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
}

Koodide võrdlemisel on erinevus palja silmaga nähtav; pööran tähelepanu nupu „Ok” töötlusele. Tk-s saab graafiliste elementide omadusi jooksvalt üle määrata valiku abil konfigureeri. Näiteks algne nuppu väljundikomando:

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

Kuid meie vormides sõltub komando vajalikust funktsionaalsusest:

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

Ülaltoodud näites on nuppu „laitse” seotud klastrite lisamisprotseduuri käivitamiseks.

Siin tasub teha kõrvalepõige graafiliste elementidega töötamisse Tk-s — erinevate andmesisendite elementide (entry, combobox, checkbutton jne) jaoks on sisse viidud selline parameeter nagu tekstimuutuja (textvariable):

entry  $frm.ent_lifetime_limit -textvariable lifetime_limit

See muutuja on määratletud globaalsetes nimespetsiifikaatorites ja sisaldab praegust sisestatud väärtust. T. e., et saada sisestatud tekst välja sisestusväljast, tuleb lihtsalt lugeda vastava muutuja väärtust (muidugi tingimusel, et see on elementide loomisel määratud).

Teine meetod sisestatud teksti saamiseks (elemendi tüübi entry jaoks) on kasutada käsku get:

.add.frm.ent_name get

Mõlemat meetodit saab näha ülaltoodud koodis.

Selle nupu vajutamine käivitab antud juhul RunCommand protseduuri koos moodustatud käsureaga klastrite lisamiseks rac-i terminoloogias:

/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

Nii oleme jõudnud peamise käsuni, mis juhib rac-i käivitamist vajalike parameetritega ning tõlgendab käsu väljundit loenditeks ja tagastab vajadusel:

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
    # avame kanal mitte blokeeriva režiimi
    # $rac - käsk täispikkust
    # $par - loodud käivitusvõtmed ja valikud
    set pipe [open "|$rac_cmd $par" "r"]
    try {
        set lst ""
        set l ""
        # käsu väljund lisame loendisse
        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} {
        # Käivitame veahalduri
        ErrorParcing $result $options
        return ""
    }
}

Peale põhiserveri andmete sisestamist lisatakse see puusse. Selle eest vastutab eelpool toodud protseduuris Add:server järgmine kood:

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

Nüüd, kui klõpsame serveri nime puus, saame loendi klastritest, mida see server haldab, ja kui klõpsame klastri peale, saame loendi klastri elementidest (serverid, andmebaasidest jne). See on realiseeritud protseduuris TreePress (fail lib/function.tcl):

proc TreePress {tree} {
   global host server active_cluster infobase
   # määrame esiletõstetud elemendi
    set id  [$tree selection]
   # seadistame vajalikud globaalmuutujad
    SetGlobalVarFromTreeItems $tree $id
   # Määrame võtme ja väärtuse, st just valitud elemendi tüübi
    set values [$tree item $id -values]
    set key [lindex [split $id "::"] 0]
   # ja sõltuvalt sellest, mida valiti, käivitatakse vastav protseduur
   # nimepiirkonnas Run
    Run::$key $tree $host $values
}

Seega käivitatakse põhiserveri puhul Run::server (klastrile — Run::cluster, tööserverile — Run::work_server jne). See tähendab, et muutuja $key väärtus on puu elemendi nime osa, mis on määratud valikuga -id.

Täpsemalt, vaatame protseduuri

Run::server

proc Run::server {tree host values} {
    # saame loetelu klastritest vajalikust serverist
    set lst [RunCommand server::$host "cluster list $host"]
    if {$lst eq ""} {return}
    set l [lindex $lst 0]
    #puts $lst
    # eemaldame nimekirjast liialdused
    .frm_work.tree_work delete  [ .frm_work.tree_work children {}]
    # loeme nimekirja
    foreach cluster_list $lst {
        # Täidame nimekirja saadud väärtustega
        InsertItemsWorkList $cluster_list
        # töötleme väljundit (nimekirja) andmete lisamiseks puusse
        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]]
            }
        }
    }
    # lisame klastrid puusse
    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"
            # lisame elementi klastrisse
            InsertClusterItems $tree $id
        }
    }
    if { [$tree exists "agent_admins::$id"] == 0 } {
        $tree insert "server::$host" end -id "agent_admins::$id" -text "Aministratorid" -values "$id"
        #InsertClusterItems $tree $id
    }
}

See protseduur töötleb serverilt saadud andmeid käsu RunCommand kaudu ja lisab erinevaid elemente puusse – klastrid, erinevad juureelemendid (andmebaasid, töötavad serverid, seansid jne). Kui tähelepanelikult vaadata, võib märgata protseduuri InsertItemsWorkList kutsumist. Seda kasutatakse elementide lisamiseks graafilisse nimekirja, töötledes varem muutuja $lst alla saadetud konsooli utiliidi rac väljundit, mis oli nimekirjana.

Näiteks klusteri ühenduste nimekiri:

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

Visuaalselt näeb see välja umbes nii:

Kirjutame GUI 1C RAC jaoks või taas Tcl/Tk-st

Ülaltoodud protseduur määrab elementide pealkirjade nimed ja andmed tabeli täitmiseks:

InsertItemsWorkList

proc InsertItemsWorkList {lst} {
    global work_list_row_count
    # stringi vaheline värvi seadmine
    if [expr $work_list_row_count % 2] {
        set tag dark
    } else {
        set tag light
    }
    # ridade parsimine võtme - väärtuse paaride kaupa
    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]
        }
    }
     # tabeli täitmine
    .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
    # pealkirjade seadmine
    foreach j $column_list {
        .frm_work.tree_work heading $j -text $j
    }
    incr work_list_row_count
}

Siin on tavalise käsu [split $str «:»], mis jagab stringi elementideks, mida eraldab «:», asemel kasutatud regulaarne avaldis, kuna mõned elemendid sisaldavad samuti koolonit.

InsertClusterItems protseduur (üks mitmest sarnasest) lihtsalt lisab puusse vajalikule elemendile cluster alla vastava tuuleelemendi nimekirja õigete identifikaatoritega.
InsertClusterItems

proc InsertClusterItems {tree id} {
    set parent "cluster::$id"
    $tree insert $parent end -id "infobases::$id" -text "Infobaasid" -values "$id"
    $tree insert $parent end -id "servers::$id" -text "Tööserverid" -values "$id"
    $tree insert $parent end -id "admins::$id" -text "Administraatorid" -values "$id"
    $tree insert $parent end -id "managers::$id" -text "Klastri juhid" -values $id
    $tree insert $parent end -id "processes::$id" -text "Töösprotsessid" -values "workprocess-all"
    $tree insert $parent end -id "sessions::$id" -text "Seansid" -values "sessions-all"
    $tree insert $parent end -id "locks::$id" -text "Lukustused" -values "blocks-all"
    $tree insert $parent end -id "connections::$id" -text "Ühendused" -values "connections-all"
    $tree insert $parent end -id "profiles::$id" -text "Turbeprofiilid" -values $id
}

Saame vaadelda veel kahte varianti sarnase protseduuri rakendamiseks, kus on selgelt näha, kuidas saab optimeerida ja vältida korduvaid käske:

Selles protseduuris on lisamine ja kontroll lahendatud otseselt:

InsertBaseItems

proc InsertBaseItems {tree id} {
    set parent "infobase::$id"
    if { [$tree exists "sessions::$id"] == 0 } {
        $tree insert $parent end -id "sessions::$id" -text "Seansid" -values "$id"
    }
    if { [$tree exists "locks::$id"] == 0 } {
        $tree insert $parent end -id "locks::$id" -text "Lukustused" -values "$id"
    }
    if { [$tree exists "connections::$id"] == 0 } {
        $tree insert $parent end -id "connections::$id" -text "Ühendused" -values "$id"
    }
}

Siin on lähenemine õigesti teostatav:

InsertProfileItems

proc InsertProfileItems {tree id} {
    set parent "profile::$id"
    set lst {
        {dir "Virtuaalsed kataloogid"}
        {com "Lubatud COM-klassid"}
        {addin "Välisharud"}
        {module "Välisharud ja aruanne"}
        {app "Lubatud rakendused"}
        {inet "Interneti ressursid"}
    }
    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 
    }
}

Erinevus nende vahel seisneb tsükli rakendamises, kus teostatakse korduv käsk (käskude). Millist lähenemist rakendada — see on juba arendaja hinnang.

Elementide lisamine ja andmete saamine oleme kaalunud, nüüd on aeg peatuda redigeerimisele. Kuna peamiselt kasutatakse redigeerimiseks ja lisamiseks samu parameetreid (va välistatud teabebaasi), kasutatakse ka dialoogivorme samu. Protseduuride väljakutsumise algoritm lisamiseks näeb välja järgmine:

Add::$key->AddToplevel

Ja redigeerimise puhul nii:

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

Näiteks vaatame klastrite redigeerimist, st klastrinime puutudes puudutame redigeerimisnuppu tööriistaribal (pliiats) ja vastav vorm kuvatakse ekraanile:

Kirjutame GUI 1C RAC jaoks või taas Tcl/Tk-st
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 ""
    }
    # joonistame klastriformi
    set frm [Add::cluster $tree $host $values]
    # muudame sildi teksti
    $frm configure -text "Klastri redigeerimine"
    
    set active_cluster $values
    # saame andmed valitud klastrilt
    set lst [RunCommand cluster::$values "cluster info --cluster=$active_cluster $host"]
    # täidame väljad
    FormFieldsDataInsert $frm $lst
    # keelame redigeerimise, mis on keelatud
    $frm.ent_host configure -state disable
    $frm.ent_port configure -state disable
    # määrame ümber töötleja
    .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
    }
}

Koodis olevate kommentaaride põhjal, on põhimõtteliselt kõik selge, välja arvatud see, et nupu töötleja on ümber määratud ja olemas on protseduur FormFieldsDataInsert, mis täidab väljad andmetega ja initsialiseerib muutujad:

FormFieldsDataInsert

proc FormFieldsDataInsert {frm lst} {
    foreach i [lindex $lst 0] {
        # saame parameetrite ja väärtuste loendi
        if [regexp -nocase -all -- {(D+)(s*?|)(:)(s*?|)(.*)} $i match param v2 v3 v4 value] {
            # muudame sümboleid
            regsub -all -- "-" [string trim $param] "_" entry_name
            # täidame andmetega
            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 """]
            }
            # checkboxide väärtused
            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
                }
            }
        }
    }
}

Selles protsessis tuli välja veel üks tcl-i eelis — muutujate nimed asendatakse teiste muutujate väärtustega. See tähendab, et vormide täitmise ja muutujate initsialiseerimise automatiseerimiseks vastavad väljade ja muutujate nimed rac utiliidi käsurea võtmetele ning käskude väljundi parameetrite nimedele, välja arvatud see, et sidekriipsud asendatakse allajoontega. Näiteks scheduled-jobs-deny vastab väljale ent_scheduled_jobs_deny ja muutujale scheduled_jobs_deny.

Vormide lisamine ja redigeerimine võivad erineda väljade koostisosade poolest, näiteks, töö infosüsteemiga:

Infosüsteemi lisamine

Kirjutame GUI 1C RAC jaoks või taas Tcl/Tk-st

Infosüsteemi redigeerimine

Kirjutame GUI 1C RAC jaoks või taas Tcl/Tk-st

Redigeerimise protseduuris Edit::infobase lisatakse vormile vajalikud väljad, kood on ulatuslik, seega ma siinkohal seda ei too.

Sarnasel viisil on rakendatud lisamise, redigeerimise, kustutamise protseduurid ka teiste elementide jaoks.

Kuna utiliidi töö hõlmab piiramatult serverite, klastrite, infosüsteemide jne arvu, siis iga serveri või infosüsteemi klastrisse kuuluvuse määramiseks on sisse viidud mitu globaalse muutujat, mille väärtused määratakse igal klõpsamisel puu elementidele. See tähendab, et protseduur läbib rekursiivselt kõik vanemate elemendid ja määrab muutujad:

SetGlobalVarFromTreeItems

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 klastri võimaldab tööd nii autoriseerimisega kui ka ilma. On olemas kaks tüüpi administraatoreid — klastrite agentide administraator ja klastrite administraator. Vastavalt sellele on sisse viidud veel 4 globaalset muutujat, mis sisaldavad administraatori kasutajanime ja parooli. See tähendab, et kui klastris on olemas administraatori konto, siis kuvatakse dialoog kasutajanime ja parooli sisestamiseks, andmed salvestatakse mällu ja sisestatakse igasse käsu vastava klastri jaoks.

Selle eest vastutab vigade käitlemise protseduur

ErrorParcing

proc ErrorParcing {err opt} {
    global cluster_user cluster_pwd agent_user agent_pwd
        switch -regexp -- $err {
        "Cluster administrator is not authenticated" {
            AuthorisationDialog "Klastri administraator"
            .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 "Klastri agendi administraator"
            .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 "Klastri administraator"
            .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 "Klastri agendi administraator"
            .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"
        }
    }
}

See, depending on what the command returns, the reaction will be accordingly.

Currently, the functionality is implemented about 95%, it remains to implement work with security profiles and test it =). That's all. I apologize for the hasty narrative.

The code, as usual, is available siit.

Update: Finished working with security profiles. Now the functionality is implemented at 100%.

Update 2: added localization in English and Russian, tested on Win7
Kirjutame GUI 1C RAC jaoks või taas Tcl/Tk-st

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster