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

Uurides 1C toodete tööprotsesse Linuxi keskkonnas, märkasin, et puudub mugav graafiline mitmeplatvormiline tööriist 1C serverite klastrite haldamiseks. Seetõttu otsustasin selle puuduse parandada, kirjutades GUI konsooli utiliidile rac. Arendamiseks valisin tcl/tk, kuna see on minu arvates kõige sobivam sellele ülesandele. Siin on mõned huvitavad aspektid lahendusest, mida soovin selles materjalis tutvustada.

Tööks on vajalikud tcl/tk ja 1C jaotused. Kuna otsustasin maksimaalselt kasutada tcl/tk põhiversiooni funktsioone ilma kolmandate osapoolte pakettide rakendamiseta, on vajalik versioon 8.6.7, kuhu kuulub ttk — pakett, mis sisaldab täiendavaid graafilisi elemente. Meile on enamasti vajalik ttk::TreeView, mis võimaldab andmeid kuvada nii puustruktuuris kui ka tabeli (loendi) vormis. Lisaks on uues versioonis muudetud, kuidas töötada eranditega (käsk try, mida projektis kasutatakse väliste käskude käivitamisel).

Projekt koosneb mitmest failist (kuigi pole midagi, mis takistaks kõike ühte failisse panna):

rac_gui.cfg — vaikimisi konfi
rac_gui.tcl — peamine käivitus skript
Kataloogis lib asuvad failid, mis laaditakse automaatselt käivitamisel:
function.tcl — protseduurifail
gui.tcl — peamine graafiline liides
images.tcl — base64 kujutiste teek

Fail rac_gui.tcl käivitab tõlgendi, initsialiseerib muutujad, laadib moodulid, konfiguratsioonid jne. Faili sisu koos 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 asjade laadimist ja utiliidi rac olemasolu kontrollimist avatakse graafiline aken. Programmi liides koosneb kolmest elemendist:

Tööriistariba, puu ja loend

Puul oleva sisu olen teinud võimalikult sarnaseks tavalise Windowsi 1C tööriistaga.

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

Peamine kood, mis selle akna moodustab, 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öökorraldus on järgmine:

1. Alustuseks tuleb lisada klastrite põhi-server (st klastrihalduse server (Linuxis käivitatakse haldus käsuga „/opt/1C/v8.3/x86_64/ras cluster —daemon“)).

Selleks klõpsake nuppu „+” ja avanenud aknas sisestage serveri aadress ja port:

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

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

2. Klusteri nimele klikkides avatakse sellele saadaval olevate funktsioonide nimekiri.

3.…

Ja nii edasi, st et uue klustrite lisamiseks valime loendis oleva, ja vajutame tööriistariba nuppu „+“, mis avab uue klustri lisamise dialooge:

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

Tööriistariba nuppude funktsioonid sõltuvad kontekstist, st mis tahes puu- või loendi elementi on valitud, vastav protseduur käivitatakse.

Vaatame lisamisnupu („+“) näidet:

Nupu genereerimise kood:

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

Siin näeme, et nupule vajutamisel käivitatakse „Add“ protseduur, mille kood:

proc Add {} {
    global active_cluster host
    # Määratleme valitud elemendi identifikaatori
    set id  [.frm_tree.tree selection] 
    # Määratleme selle elemendi väärtuse
    set values [.frm_tree.tree item [.frm_tree.tree selection] -values]
    set key [lindex [split $id "::"] 0]
    # olenevalt valikust käivitatakse vajalik protseduur
    if {$key eq "" || $key eq "server"} {
        set host [ Add::server ]
        return
    }
    Add::$key .frm_tree.tree $host $values
}

Siin paistab üks tikli eelis — protseduuri nime asemel võib edastada muutuja väärtuse:

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

See, näiteks, kui me tihendame põhiserverit ja vajutame " + ", siis käivitatakse protseduur Add::server. Kui klastrisse — Add::cluster ja nii edasi (kust saavad vajalikud "võtmed", kirjutan veidi hiljem). Loetletud protseduurid joonistavad konteksti vastavad graafilised elemendid.

Kuidas olete juba võinud märgata, on vormid stiililt sarnased — see ei ole üllatav, sest need kuvatakse ühe protseduuri kaudu, täpsemalt vormi põhiraam (aken, nupud, pilt, silt), protseduuri nimetus. 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äljadega
    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 nuppen
    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
}

Kutsungiparameetrid: pealkiri, ikoonipildi nimi raamatukogust (lib/images.tcl) ja valikuline aknanimi (vaikimisi .add). Seega, kui võtta ülaltoodud näited põhiserveri ja klastrite lisamiseks, siis kutse oleks vastavalt:

AddToplevel "Põhiserveri 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 peamise vormi
    set frm [AddToplevel "Peamise serveri lisamine" server_grey_64]
    # lisame sildid ja sisestusväljad 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]
    # uuendame nupu vajutamise käsitlejat
    .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 "Klusteri lisamine" cluster_grey_64]
    
    label $frm.lbl_host -text "Põhiserveri 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 "Klusteri nimi"
    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 "Peatada välja lülitatud 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 serverivea kõrvalekalle, %"
    entry  $frm.ent_errors_count_threshold -textvariable errors_count_threshold
    label $frm.lbl_processes -text "Tööpordud:"
    label $frm.lbl_lifetime_limit -text "Taaskäivitamise periood, sek."
    entry  $frm.ent_lifetime_limit -textvariable lifetime_limit
    label $frm.lbl_max_memory_size -text "Lubatud mälu suurus, KB"
    entry  $frm.ent_max_memory_size -textvariable max_memory_size
    label $frm.lbl_max_memory_time_limit -text "Lubatud mälu ületamise kestus, sek."
    entry  $frm.ent_max_memory_time_limit -textvariable max_memory_time_limit
    label $frm.lbl_kill_problem_processes -justify left -anchor nw -text "Sunniviisiliselt lõpetada probleemsed protsessid"
    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
    # üle kirjutame töötleja
    .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
}

Kuna võrreldes nende protseduuride koodiga on erinevust palja silmaga näha, keskendun nuppude töötleja "Ok" poole. Tk-s saab graafiliste elementide omadusi käitamise ajal ümber määrata valiku abil. configure. Näiteks, algne nuppu väljastamise käsk:

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

Kuid meie vormides sõltub käsk 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 nupp "zabito" käivitanud klastrite lisamise protseduuri.

Siin tasub teha kõrvalpöörde graafiliste elementide tööde juurde Tk-s — erinevate andmesisendi elementide (entry, combobox, checkbutton jne) jaoks on sisse viidud selline parameeter nagu tekstimuutuja (textvariable):

sisestus  $frm.ent_lifetime_limit -textvariable lifetime_limit

See muutuja on määratletud globaalsetes nimede ruumides ja sisaldab praegust sisestatud väärtust. See tähendab, et teksti saamiseks sisendväljast tuleb lihtsalt lugeda vastava muutuja väärtus (loomulikult tingimusel, et see on elementide loomisel määratletud).

Teine meetod sisestatud teksti saamiseks (sisendelementide puhul) on käsu get kasutamine:

.add.frm.ent_name get

Mõlemad need meetodid on nähtavad ülaltoodud koodis.

Selle nupu vajutamine käivitab antud juhul RunCommand protseduuri koos genereeritud klastrite lisamise käsureaga rac terminite järgi:

/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

Nüüd oleme jõudnud põhikäsuni, mis haldab rac-i käivitamist vajalike parameetritega, samuti analüüsib käsu väljundit nimekirjadeks ja tagastab, kui see on vajalik:

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 mitteblokeerivas režiimis
    # $rac - käsk täisfailitee
    # $par - koostatud 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äivita vigade käsitleja
        ErrorParcing $result $options
        return ""
    }
}

Peale põhiserveri andmete sisestamist lisatakse see puusse, selle eest vastutab ülaltoodud 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 nimekirja klustritest, mida selle serveri kaudu hallatakse, ja kui klõpsame klustri peal, saame klustrite elementide nimekirja (serverite, teabebaasid jne). See on realiseeritud protseduuris TreePress (fail lib/function.tcl):

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

Seega käivitub põhiserveri jaoks Run::server (kliustrite jaoks — Run::cluster, töös serveri jaoks — Run::work_server jne). T.d. muutuja $key väärtus on osa puu nime, mis määratakse valiku kaudu. -id.

Jätkame protseduuri

Run::server

proc Run::server {tree host values} {
    # saame nimekirja klastritest vajaliku serveri kohta
    set lst [RunCommand server::$host "cluster list $host"]
    if {$lst eq ""} {return}
    set l [lindex $lst 0]
    # eemaldame lisad nimekirjast
    .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 (nimekiri), et lisada andmed 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 elemendid klastrisse
            InsertClusterItems $tree $id
        }
    }
    if { [$tree exists "agent_admins::$id"] == 0 } {
        $tree insert "server::$host" end -id "agent_admins::$id" -text "Administraatorid" -values "$id"
        #InsertClusterItems $tree $id
    }
}

See protseduur töötleb seda, mis oli saadud serverilt käsu RunCommand kaudu, ja lisab erinevaid elemente puusse — klastreid, erinevaid juurelemente (andmebaasid, tööserverid, seansid jne). Kui lähemalt vaadata, siis seest võib näha protseduuri InsertItemsWorkList kutsumist. Seda kasutatakse elementide lisamiseks graafilisse nimekirja, töötledes konsoli utiliidi rac väljundit, mis varem oli nimekirjana muutuja $lst tagasi saadetud. See on nimekiri nimekirjadest, mis sisaldavad paaride elemente, mida eraldab koolon.

Näiteks klusterü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

Graafiliselt näeb see välja umbes nii:

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

Ülaltoodud protseduur määrab elemendi nimed pealkirjaks ja andmed tabeli täitmiseks:

InsertItemsWorkList

proc InsertItemsWorkList {lst} {
    global work_list_row_count
    # ridade värvi vahetamise seadmine
    if [expr $work_list_row_count % 2] {
        set tag dark
    } else {
        set tag light
    }
    # ridade analüüs võtme - väärtuse paarideks
    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
    # päiste seadmine
    foreach j $column_list {
        .frm_work.tree_work heading $j -text $j
    }
    incr work_list_row_count
}

Siin on lihtsa käsu [split $str «:»] asemel, mis jagab rea elementideks, mis on eraldatud «:», ja tagastab loendi, kasutatud regulaaravaldis, kuna mõned elemendid sisaldavad samuti koolonit.

Protseduur InsertClusterItems (üks mitmest sarnasest) liidab lihtsalt vajalikku elemendi cluster alla loetelu alamelementidest vastavate 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öös serverid" -values "$id"
    $tree insert $parent end -id "admins::$id" -text "Administratorid" -values "$id"
    $tree insert $parent end -id "managers::$id" -text "Klastri juhid" -values $id
    $tree insert $parent end -id "processes::$id" -text "Tööprotsessid" -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 "Turvaprofiilid" -values $id
}

On võimalik kaaluda ka kahte alternatiivset viisi sellise protseduuri rakendamiseks, kus on selgelt näha, kuidas optimeerida ja vabaneda korduvatest käskudest:

Käesolevas protseduuris on lisamine ja kontroll lahendatud otse:

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:

InsertProfileItems

proc InsertProfileItems {tree id} {
    set parent "profile::$id"
    set lst {
        {dir "Virtuaalsed kataloogid"}
        {com "Lubatud COM-klassid"}
        {addin "Välised komponendid"}
        {module "Välised aruanded ja töötlemised"}
        {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 
    }
}

Nende vahel on erinevus tsükli rakendamises, kus toimub korduv käsk (käskude). Millist lähenemist rakendada — see on juba arendaja vabaduses.

Elementide lisamine ja andmete saamine on käsitletud, nüüd on aeg peatuda redigeerimisele. Kuna põhimõtteliselt kasutatakse redigeerimiseks ja lisamiseks samu parameetreid (välja arvatud teabebaas), siis on dialoogivormid samad. Protseduuride kutsumise algoritm lisamiseks näeb välja järgmine:

Add::$key->AddToplevel

Ja redigeerimise korral nii:

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

Võtame näiteks klastri redigeerimise, st klõpsame puus klastri nimele, vajutame redigeerimise nuppu tööriistaribal (pliiats) ja ekraanile kuvatakse vastav vorm:

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 ""
    }
    # kuvame klastrit
    set frm [Add::cluster $tree $host $values]
    # muudame sildi teksti
    $frm configure -text "Klastri redigeerimine"
    
    set active_cluster $values
    # saame valitud kasti andmed
    set lst [RunCommand cluster::$values "cluster info --cluster=$active_cluster $host"]
    # täidame väljad
    FormFieldsDataInsert $frm $lst
    # keelame väljad, mille muutmine on keelatud
    $frm.ent_host configure -state disable
    $frm.ent_port configure -state disable
    # määrame töötleja uuesti
    .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
    }
}

Koodi kommentaarides on põhimõtteliselt kõik selge, välja arvatud see, et nupu töötleja on üle 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] {
            # muuda sümboleid
            regsub -all -- "-" [string trim $param] "_" entry_name
            # täida 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 """]
            }
            # märkeruutude jaoks muuda väärtusi
            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 protseduuris tuli esile veel üks tcl'i eelis — muutujate nimed asendatakse teiste muutujate väärtustega. See tähendab, et vormide automaatseks täitmiseks ja muutujate algatamiseks vastavad väljade ja muutujate nimed utiliidi rac käsuridade võtmetele ning käskude väljundparameetrite nimedele, teatud eranditega — sidekriips on asendatud alajaotusega. Näiteks scheduled-jobs-deny vastab väljal ent_scheduled_jobs_deny ja muutujale scheduled_jobs_deny.

Lisamise ja redigeerimise vormid võivad erineda väljade koosseisu poolest, näiteks, töötamine infobaasiga:

IB lisamine

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

IB redigeerimine

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

Redigeerimismeetodis Edit::infobase lisatakse vormile vajalikud väljad, kood on ulatuslik, seetõttu ma seda siin ei tooda.

Analoogiliselt on rakendatud ka lisamise, redigeerimise ja kustutamise protseduurid teiste elementide jaoks.

Kuna utiliidi töö hõlmab piiramatut arvu servereid, klastreid, andmebaase jne, on sisse viidud mitmed globaalsed muutujad, mille väärtused määratakse iga kord, kui klikkida puu elementidel. See tähendab, et protseduur läbib retsursiivselt kõik vanemad 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 klaster võimaldab töötada nii autoriseerimisega kui ka ilma. On kaks tüüpi administraatoreid — klasteragendi administraator ja klastrite administraator. Vastavalt sellele on tööks sisse viidud veel 4 globaalset muutujat, mis sisaldavad administraatori kasutajanime ja parooli. St kui klastris on administreerimise konto, siis kuvatakse dialoog logi ja parooli sisestamiseks, andmed salvestatakse mällu ja sisestatakse iga vastava klastrite käsu juurde.

Selle eest vastutab vigade töötlemise protseduur.

ErrorParcing

proc ErrorParcing {err opt} {
    global cluster_user cluster_pwd agent_user agent_pwd
        switch -regexp -- $err {
        "Klastri administraator ei ole autentitud" {
            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
        }
        "Keskserveri administraator ei ole autentitud" {
            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, vastavalt sellele, mida käsk tagastab, on ka reaktsioon vastav.

Praeguseks on funktsionaalsus umbes 95% ulatuses teostatud, on jäänud realiseerida töö turvaprofili kallal ning testida =). Sellega on kõik. Vabandust kokkusurutud jutustuse pärast.

Kood on traditsiooniliselt saadaval siin.

Uuendus: Lõpetasin turvaprofili töö. Nüüd on funktsionaalsus 100% ulatuses teostatud.

Uuendus 2: ingliskeelne ja venekeelne lokaliseerimine on lisatud, kontrollitud töö Windows 7 keskkonnas.
Kirjutame GUI 1C RAC jaoks, või taas Tcl/Tk-st.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster