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

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.
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_32Siit 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 $valuesSt, 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_64või
AddToplevel "Klastri lisamine" cluster_grey_64Ja 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_limitSee 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 getMõ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:1545Nii 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:
Ü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:

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

Infosüsteemi redigeerimine

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 .
Update: Finished working with security profiles. Now the functionality is implemented at 100%.
Update 2: added localization in English and Russian, tested on Win7

Allikas: habr.com
