Wir schreiben GUI an 1C RAC oder wieder über Tcl / Tk

Als ich mich mit dem Thema der Arbeit mit 1C-Produkten unter Linux befasste, entdeckte ich einen Nachteil - das Fehlen eines praktischen grafischen Multi-Plattform-Tools zum Verwalten eines Clusters von 1C-Servern. Und es wurde beschlossen, dieses Manko zu beheben, indem eine GUI für das Konsolendienstprogramm rac geschrieben wurde. Die Entwicklungssprache wurde meiner Meinung nach als die für diese Aufgabe am besten geeignete gewählt. Und jetzt möchte ich einige interessante Aspekte der Lösung in diesem Material vorstellen.

Für die Arbeit benötigen Sie tcl / tk- und 1C-Verteilungen. Und da ich mich entschlossen habe, die Funktionen der grundlegenden tcl / tk-Bereitstellung optimal zu nutzen, ohne Pakete von Drittanbietern zu verwenden, benötige ich Version 8.6.7, die ttk enthält - ein Paket mit zusätzlichen grafischen Elementen, von denen wir hauptsächlich ttk :: TreeView benötigen Daten sowohl in Form einer Baumstruktur als auch in Form einer Tabelle (Liste) anzeigen. Außerdem wurde in der neuen Version die Arbeit mit Ausnahmen wiederholt (der Befehl try, der im Projekt verwendet wird, wenn externe Befehle ausgeführt werden).

Ein Projekt besteht aus mehreren Dateien (obwohl nichts verhindert, dass alles von einer erledigt wird):

rac_gui.cfg - Standardkonfiguration
rac_gui.tcl - Hauptstart-Skript
Das lib-Verzeichnis enthält Dateien, die beim Start automatisch geladen werden:
function.tcl - Datei mit Prozeduren
gui.tcl - Haupt-GUI
images.tcl - Bildbibliothek in base64

Die Datei rac_gui.tcl startet tatsächlich den Interpreter, initialisiert die Variablen, lädt die Module, Konfigurationen usw. Der Inhalt der Datei mit Kommentaren:

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


Nachdem Sie alles Notwendige heruntergeladen und die Verfügbarkeit des Dienstprogramms rac überprüft haben, wird ein grafisches Fenster geöffnet. Die Programmoberfläche besteht aus drei Elementen:

Symbolleiste, Baum und Liste

Ich habe den Inhalt des "Baums" so ähnlich wie möglich zu einem Standard-Windows-Snap von 1C gemacht.

Bild

Der Hauptcode, der dieses Fenster bildet, ist in der Datei enthalten
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 


Der Algorithmus für die Arbeit mit dem Programm lautet wie folgt:

1. Zu Beginn müssen Sie den Hauptclusterserver hinzufügen (dh den Clusterverwaltungsserver (unter Linux wird der Befehl mit dem Befehl "/opt/1C/v8.3/x86_64/ras cluster --daemon" gestartet).

Klicken Sie dazu auf die Schaltfläche "+" und geben Sie im folgenden Fenster die Serveradresse und den Port ein:

Bild

Danach wird unser Server in der Baumstruktur angezeigt, indem Sie darauf klicken. Eine Liste der Cluster wird geöffnet oder ein Verbindungsfehler wird angezeigt.

2. Durch Klicken auf den Clusternamen wird eine Liste der dafür verfügbaren Funktionen geöffnet.

3. ...

Gut und so weiter, d.h. Um einen neuen Cluster hinzuzufügen, wählen Sie einen in der Liste verfügbaren aus und klicken Sie auf die Schaltfläche + in der Symbolleiste. Der Dialog zum Hinzufügen eines neuen Clusters wird angezeigt:

Bild

Die Schaltflächen in der Symbolleiste führen Funktionen abhängig vom Kontext aus, d. H. Aus welchem ​​Element des Baums oder der Liste ausgewählt wird, wird dieses oder jenes Verfahren ausgeführt.

Betrachten Sie die Schaltfläche zum Hinzufügen ("+") als Beispiel:

Code zur Generierung von Schaltflächen:

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

Hier sehen wir, dass beim Drücken der Schaltfläche die Add-Prozedur ausgeführt wird, deren Code:

 proc Add {} { global active_cluster host #     set id [.frm_tree.tree selection] #     set values [.frm_tree.tree item [.frm_tree.tree selection] -values] set key [lindex [split $id "::"] 0] #           if {$key eq "" || $key eq "server"} { set host [ Add::server ] return } Add::$key .frm_tree.tree $host $values } 

Einer der Vorteile des Tickles ist also sichtbar: Sie können den Wert einer Variablen als Namen der Prozedur übergeben:

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

Das heißt, wenn wir zum Beispiel auf dem Hauptserver stöbern und "+" drücken, wird die Add :: Server-Prozedur gestartet, wenn der Cluster Add :: Cluster ist und so weiter (ich werde ein wenig darüber schreiben, woher die erforderlichen "Schlüssel" kommen unten) zeichnen die aufgelisteten Prozeduren grafische Elemente, die dem Kontext entsprechen.

Wie Sie vielleicht bemerkt haben, sind die Formulare im Stil ähnlich - dies ist nicht überraschend, da sie in einer Prozedur angezeigt werden, genauer gesagt im Hauptrahmen des Formulars (Fenster, Schaltflächen, Bild, Beschriftung), dem Namen der Prozedur
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 #    ttk::label $win_name.lbl -image $img #     set frm [ttk::labelframe $win_name.frm -text $lbl -labelanchor nw] grid columnconfigure $frm 0 -weight 1 grid rowconfigure $frm 0 -weight 1 #    set frm_btn [frame $win_name.frm_btn -border 0] ttk::button $frm_btn.btn_ok -image ok_grey_24 -command { } ttk::button $frm_btn.btn_cancel -command $cmd -image quit_grey_24 grid $win_name.lbl -row 0 -column 0 -sticky nw -padx 5 -pady 10 grid $frm -row 0 -column 1 -sticky nw -padx 5 -pady 5 grid $frm_btn -row 1 -column 1 -sticky se -padx 5 -pady 5 pack $frm_btn.btn_cancel -side right pack $frm_btn.btn_ok -side right -padx 10 return $frm } 


Aufrufparameter: Titel, Bildname für das Symbol aus der Bibliothek (lib / images.tcl) und der optionale Fensternamenparameter (Standard .add). Wenn wir also die obigen Beispiele verwenden, um den Hauptserver und den Cluster hinzuzufügen, lautet der Aufruf entsprechend:

 AddToplevel "  " server_grey_64 

oder

 AddToplevel " " cluster_grey_64 

Wenn Sie mit diesen Beispielen fortfahren, werde ich die Prozeduren zeigen, mit denen die Hinzufügen-Dialoge für den Server oder Cluster angezeigt werden.

Add :: server
 proc Add::server {} { global default #    set frm [AddToplevel "  " server_grey_64] #         label $frm.lbl_host -text " " entry $frm.ent_host label $frm.lbl_port -text "" entry $frm.ent_port $frm.ent_port insert end $default(port) grid $frm.lbl_host -row 0 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.ent_host -row 0 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_port -row 1 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.ent_port -row 1 -column 1 -sticky nsew -padx 5 -pady 5 grid columnconfigure $frm 0 -weight 1 grid rowconfigure $frm 0 -weight 1 #set frm_btn [frame .add.frm_btn -border 0] #     .add.frm_btn.btn_ok configure -command { set host [SaveMainServer [.add.frm.ent_host get] [.add.frm.ent_port get]] .frm_tree.tree insert {} end -id "server::$host" -text "$host" -values "$host" destroy .add return $host } return $frm } 


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 "" } #    () set lifetime_limit $default(lifetime_limit) set expiration_timeout $default(expiration_timeout) set session_fault_tolerance_level $default(session_fault_tolerance_level) set max_memory_size $default(max_memory_size) set max_memory_time_limit $default(max_memory_time_limit) set errors_count_threshold $default(errors_count_threshold) set security_level [lindex $default(security_level) 0] set load_balancing_mode [lindex $default(load_balancing_mode) 0] set frm [AddToplevel " " cluster_grey_64] label $frm.lbl_host -text "  " entry $frm.ent_host label $frm.lbl_port -text "" entry $frm.ent_port $frm.ent_port insert end $default(port) label $frm.lbl_name -text " " entry $frm.ent_name label $frm.lbl_secure_connect -text " " ttk::combobox $frm.cb_security_level -textvariable security_level -values $default(security_level) label $frm.lbl_expiration_timeout -text "   :" entry $frm.ent_expiration_timeout -textvariable expiration_timeout label $frm.lbl_session_fault_tolerance_level -text " " entry $frm.ent_session_fault_tolerance_level -textvariable session_fault_tolerance_level label $frm.lbl_load_balancing_mode -text "  " ttk::combobox $frm.cb_load_balancing_mode -textvariable load_balancing_mode \ -values $default(load_balancing_mode) label $frm.lbl_errors_count_threshold -text "    , %" entry $frm.ent_errors_count_threshold -textvariable errors_count_threshold label $frm.lbl_processes -text " :" label $frm.lbl_lifetime_limit -text " , ." entry $frm.ent_lifetime_limit -textvariable lifetime_limit label $frm.lbl_max_memory_size -text "  , " entry $frm.ent_max_memory_size -textvariable max_memory_size label $frm.lbl_max_memory_time_limit -text "    , ." entry $frm.ent_max_memory_time_limit -textvariable max_memory_time_limit label $frm.lbl_kill_problem_processes -justify left -anchor nw -text "   " checkbutton $frm.check_kill_problem_processes -variable kill_problem_processes -onvalue yes -offvalue no grid $frm.lbl_host -row 0 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.ent_host -row 0 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_port -row 1 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.ent_port -row 1 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_name -row 2 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.ent_name -row 2 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_secure_connect -row 3 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.cb_security_level -row 3 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_expiration_timeout -row 4 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.ent_expiration_timeout -row 4 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_session_fault_tolerance_level -row 5 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.ent_session_fault_tolerance_level -row 5 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_load_balancing_mode -row 6 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.cb_load_balancing_mode -row 6 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_errors_count_threshold -row 7 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.ent_errors_count_threshold -row 7 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_processes -row 8 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.lbl_lifetime_limit -row 9 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.ent_lifetime_limit -row 9 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_max_memory_size -row 10 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.ent_max_memory_size -row 10 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_max_memory_time_limit -row 11 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.ent_max_memory_time_limit -row 11 -column 1 -sticky nsew -padx 5 -pady 5 grid $frm.lbl_kill_problem_processes -row 12 -column 0 -sticky nw -padx 5 -pady 5 grid $frm.check_kill_problem_processes -row 12 -column 1 -sticky nw -padx 5 -pady 5 #   .add.frm_btn.btn_ok configure -command { RunCommand "" "cluster insert \ --host=[.add.frm.ent_host get] \ --port=[.add.frm.ent_port get] \ --name=[.add.frm.ent_name get] \ --expiration-timeout=$expiration_timeout \ --lifetime-limit=$lifetime_limit \ --max-memory-size=$max_memory_size \ --max-memory-time-limit=$max_memory_time_limit \ --security-level=$security_level \ --session-fault-tolerance-level=$session_fault_tolerance_level \ --load-balancing-mode=$load_balancing_mode \ --errors-count-threshold=$errors_count_threshold \ --kill-problem-processes=$kill_problem_processes \ $auth_agent $host" Run::server $tree $host "" destroy .add } return $frm } 


Beim Vergleich des Codes dieser Prozeduren ist der Unterschied mit bloßem Auge sichtbar. Ich werde mich auf den Handler der OK-Schaltfläche konzentrieren. In Tk können die Eigenschaften von Grafikelementen zur Laufzeit mithilfe der Option configure neu definiert werden. Beispiel: Der Befehl zur Ausgabe der ersten Schaltfläche:

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

In unseren Formularen hängt der Befehl jedoch von der erforderlichen Funktionalität ab:

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

Im obigen Beispiel startet die Schaltfläche "Verstopft" die Prozedur zum Hinzufügen eines Clusters.

Hier lohnt es sich, einen Exkurs zur Arbeit mit grafischen Elementen in Tk zu machen - für verschiedene Dateneingabeelemente (Eingabe, Combobox, Checkbutton usw.) wurde ein Parameter wie eine Textvariable (Textvariable) eingeführt:

 entry $frm.ent_lifetime_limit -textvariable lifetime_limit 

Diese Variable ist im globalen Namespace definiert und enthält den aktuell eingegebenen Wert. Das heißt, Um den eingegebenen Text aus dem Feld zu erhalten, müssen Sie nur den Wert lesen, der der Variablen entspricht (vorausgesetzt, er wird beim Erstellen des Elements definiert).

Die zweite Methode zum Abrufen des eingegebenen Textes (für Elemente vom Typ Typ) ist die Verwendung des Befehls get:

 .add.frm.ent_name get 

Beide Methoden sind im obigen Code zu sehen.

Wenn Sie in diesem Fall auf diese Schaltfläche klicken, wird die RunCommand-Prozedur mit der generierten Befehlszeile zum Hinzufügen eines Clusters in Bezug auf rac gestartet:

 /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 

Also kamen wir zum Hauptbefehl, der den Start von rac mit den benötigten Parametern steuert, die Ausgabe der Befehle in Listen analysiert und bei Bedarf zurückgibt:

Runcommand
 proc RunCommand {root par} { global dir rac_cmd cluster work_list_row_count agent_user agent_pwd cluster_user cluster_pwd puts "$rac_cmd $par" set work_list_row_count 0 #      # $rac -     # $par -      set pipe [open "|$rac_cmd $par" "r"] try { set lst "" set l "" #       while {[gets $pipe line]>=0} { #puts $line if {$line eq ""} { lappend l $lst set lst "" } else { lappend lst [string trim $line] } } close $pipe return $l } on error {result options} { #    ErrorParcing $result $options return "" } } 


Nach Eingabe der Daten des Hauptservers werden diese dem Baum hinzugefügt. In der obigen Prozedur Hinzufügen: Server ist der folgende Code dafür verantwortlich:

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

Wenn Sie nun auf den Servernamen in der Baumstruktur klicken, wird eine Liste der von diesem Server verwalteten Cluster angezeigt. Wenn Sie auf einen Cluster klicken, wird eine Liste der Clusterelemente (Server, Infobasen usw.) angezeigt. Dies ist in der TreePress-Prozedur (Datei lib / function.tcl) implementiert:

 proc TreePress {tree} { global host server active_cluster infobase #    set id [$tree selection] #     SetGlobalVarFromTreeItems $tree $id #    , ..     set values [$tree item $id -values] set key [lindex [split $id "::"] 0] #            #    Run Run::$key $tree $host $values } 

Dementsprechend wird Run :: server für den Hauptserver gestartet (Run :: cluster für den Cluster, Run :: work_server usw. für den Produktionsserver). Das heißt, Der Wert der Variablen $ key ist Teil des Baumelementnamens, der durch die Option -id angegeben wird.

Achten Sie auf das Verfahren

Führen Sie :: server aus
 proc Run::server {tree host values} { #      set lst [RunCommand server::$host "cluster list $host"] if {$lst eq ""} {return} set l [lindex $lst 0] #puts $lst #     .frm_work.tree_work delete [ .frm_work.tree_work children {}] #   foreach cluster_list $lst { #     InsertItemsWorkList $cluster_list #   ()      foreach i $cluster_list { #puts $i set cluster_list [split $i ":"] if {[string trim [lindex $cluster_list 0]] eq "cluster"} { set cluster_id [string trim [lindex $cluster_list 1]] lappend cluster($cluster_id) $cluster_id } if {[string trim [lindex $cluster_list 0]] eq "name"} { lappend cluster($cluster_id) [string trim [lindex $cluster_list 1]] } } } #     foreach x [array names cluster] { set id [lindex $cluster($x) 0] if { [$tree exists "cluster::$id"] == 0 } { $tree insert "server::$host" end -id "cluster::$id" -text "[lindex $cluster($x) 1]" -values "$id" #     InsertClusterItems $tree $id } } if { [$tree exists "agent_admins::$id"] == 0 } { $tree insert "server::$host" end -id "agent_admins::$id" -text "" -values "$id" #InsertClusterItems $tree $id } } 


Diese Prozedur verarbeitet, was vom Server über den Befehl RunCommand empfangen wurde, und fügt dem Baum alle möglichen Dinge hinzu - Cluster, verschiedene Stammelemente (Datenbanken, Produktionsserver, Sitzungen usw.). Wenn Sie genau hinschauen, können Sie im Inneren den Aufruf der InsertItemsWorkList-Prozedur bemerken. Es wird verwendet, um der Grafikliste Elemente hinzuzufügen und die Ausgabe des Konsolendienstprogramms rac zu verarbeiten, das zuvor als Liste an die Variable $ lst zurückgegeben wurde. Dies ist eine Liste von Listen, die Elementpaare enthalten, die durch einen Doppelpunkt getrennt sind.

Zum Beispiel eine Liste von Clusterverbindungen:

 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 

In grafischer Form sieht es ungefähr so ​​aus:

Bild

Das obige Verfahren identifiziert die Namen der Elemente für den Header und die Daten, die die Tabelle füllen sollen:

InsertItemsWorkList
 proc InsertItemsWorkList {lst} { global work_list_row_count #      if [expr $work_list_row_count % 2] { set tag dark } else { set tag light } #      -  foreach i $lst { if [regexp -nocase -all -- {(\D+)(\s*?|)(:)(\s*?|)(.*)} $i match param v2 v3 v4 value] { lappend column_list [string trim $param] lappend value_list [string trim $value] } } #   .frm_work.tree_work configure -columns $column_list -displaycolumns $column_list .frm_work.tree_work insert {} end -values $value_list -tags $tag .frm_work.tree_work column #0 -stretch #   foreach j $column_list { .frm_work.tree_work heading $j -text $j } incr work_list_row_count } 


Hier wird anstelle des einfachen Befehls [split $ str ":"], der die Zeichenfolge in durch ":" getrennte Elemente aufteilt und eine Liste zurückgibt, ein regulärer Ausdruck angewendet, da einige Elemente auch einen Doppelpunkt enthalten.

Die InsertClusterItems-Prozedur (eine von mehreren ähnlichen) fügt dem erforderlichen Clusterelement in der Baumstruktur einfach eine Liste untergeordneter Elemente mit den entsprechenden Bezeichnern hinzu
InsertClusterItems
 proc InsertClusterItems {tree id} { set parent "cluster::$id" $tree insert $parent end -id "infobases::$id" -text " " -values "$id" $tree insert $parent end -id "servers::$id" -text " " -values "$id" $tree insert $parent end -id "admins::$id" -text "" -values "$id" $tree insert $parent end -id "managers::$id" -text " " -values $id $tree insert $parent end -id "processes::$id" -text " " -values "workprocess-all" $tree insert $parent end -id "sessions::$id" -text "" -values "sessions-all" $tree insert $parent end -id "locks::$id" -text "" -values "blocks-all" $tree insert $parent end -id "connections::$id" -text "" -values "connections-all" $tree insert $parent end -id "profiles::$id" -text " " -values $id } 


Sie können zwei weitere Optionen für die Implementierung eines solchen Verfahrens in Betracht ziehen, bei denen deutlich sichtbar wird, wie Sie doppelte Befehle optimieren und entfernen können:

Bei diesem Verfahren werden das Hinzufügen und Überprüfen auf der Stirn gelöst:

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


Und hier ist der Ansatz korrekter:

InsertProfileItems
 proc InsertProfileItems {tree id} { set parent "profile::$id" set lst { {dir " "} {com " COM-"} {addin " "} {module "   "} {app " "} {inet " "} } foreach i $lst { append item [lindex $i 0] "::$id" if { [$tree exists $item] == 0 } { $tree insert $parent end -id $item -text [lindex $i 1] -values "$id" } unset item } } 


Der Unterschied zwischen ihnen besteht in der Verwendung des Zyklus, in dem die wiederholten Befehle ausgeführt werden. Welcher Ansatz zu verwenden ist, liegt im Ermessen des Entwicklers.

Wir haben darüber nachgedacht, Elemente hinzuzufügen und Daten zu empfangen. Es ist Zeit, mit der Bearbeitung aufzuhören. Da zum Bearbeiten und Hinzufügen grundsätzlich dieselben Parameter verwendet werden (Ausnahme ist die Infobase), sind die Dialogformulare identisch. Die Prozeduraufrufprozedur zum Hinzufügen sieht folgendermaßen aus:

Add :: $ key-> AddToplevel

Und für die Bearbeitung wie folgt:

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

Nehmen Sie zum Beispiel die Bearbeitung eines Clusters, d. H. Durch Klicken auf den Clusternamen in der Baumstruktur drücken Sie die Schaltfläche Bearbeiten in der Symbolleiste (Bleistift). Das entsprechende Formular wird angezeigt:

Bild
Edit :: cluster
 proc Edit::cluster {tree host values} { global default lifetime_limit expiration_timeout session_fault_tolerance_level global max_memory_size max_memory_time_limit errors_count_threshold security_level global load_balancing_mode kill_problem_processes active_cluster \ agent_user agent_pwd cluster_user cluster_pwd auth if {$cluster_user ne "" && $cluster_pwd ne ""} { set auth "--cluster-user=$cluster_user --cluster-pwd=$cluster_pwd" } else { set auth "" } #     set frm [Add::cluster $tree $host $values] #     $frm configure -text " " set active_cluster $values #      set lst [RunCommand cluster::$values "cluster info --cluster=$active_cluster $host"] #   FormFieldsDataInsert $frm $lst #  ,    $frm.ent_host configure -state disable $frm.ent_port configure -state disable #   .add.frm_btn.btn_ok configure -command { RunCommand "" "cluster update \ --cluster=$active_cluster $auth \ --name=[.add.frm.ent_name get] \ --expiration-timeout=$expiration_timeout \ --lifetime-limit=$lifetime_limit \ --max-memory-size=$max_memory_size \ --max-memory-time-limit=$max_memory_time_limit \ --security-level=$security_level \ --session-fault-tolerance-level=$session_fault_tolerance_level \ --load-balancing-mode=$load_balancing_mode \ --errors-count-threshold=$errors_count_threshold \ --kill-problem-processes=$kill_problem_processes \ $auth $host" $tree delete "cluster::$active_cluster" Run::server $tree $host "" destroy .add } } 


Gemäß den Kommentaren im Code ist im Prinzip alles klar, außer dass der Code des Schaltflächenhandlers neu definiert wird und die Prozedur FormFieldsDataInsert vorhanden ist, die die Felder mit Daten füllt und die Variablen initialisiert:

FormFieldsDataInsert
 proc FormFieldsDataInsert {frm lst} { foreach i [lindex $lst 0] { #      if [regexp -nocase -all -- {(\D+)(\s*?|)(:)(\s*?|)(.*)} $i match param v2 v3 v4 value] { #   regsub -all -- "-" [string trim $param] "_" entry_name #   if [winfo exists $frm.ent_$entry_name] { $frm.ent_$entry_name delete 0 end $frm.ent_$entry_name insert end [string trim $value "\""] } if [winfo exists $frm.cb_$entry_name] { global $entry_name set $entry_name [string trim $value "\""] } #     if [winfo exists $frm.check_$entry_name] { global $entry_name if {$value eq "0"} { set $entry_name no } elseif {$value eq "1"} { set $entry_name yes } else { set $entry_name $value } } } } } 


In dieser Prozedur tauchte ein weiteres Plus tcl auf - die Werte anderer Variablen werden als Variablennamen ersetzt. Das heißt,Um das Ausfüllen von Formularen und das Initialisieren von Variablen zu automatisieren, entsprechen die Namen von Feldern und Variablen mit einigen Ausnahmen den Befehlszeilenschaltern des Dienstprogramms rac und den Namen der Ausgabeparameter von Befehlen. Der Bindestrich wird durch einen Unterstrich ersetzt. Beispielsweise entspricht Scheduled-Jobs- Deny dem Feld ent_scheduled_jobs_deny und der Variablen Scheduled_jobs_deny .

Formen des Hinzufügens und Bearbeitens können sich in der Zusammensetzung der Felder unterscheiden, z. B. beim Arbeiten mit der Informationsbasis:

Hinzufügen von IB

Bild

Bearbeiten von IB

Bild

Im Bearbeitungsverfahren Edit :: infobase werden die erforderlichen Felder zum Formular hinzugefügt, daher gebe ich hier keinen großen Code an.

Analog werden die Verfahren zum Hinzufügen, Bearbeiten und Löschen für andere Elemente implementiert.

Da das Dienstprogramm eine unbegrenzte Anzahl von Servern, Clustern, Infobasen usw. umfasst, um zu bestimmen, zu welchem ​​Cluster der Server oder die Informationssicherheit gehört, wurden mehrere globale Variablen eingeführt, deren Werte bei jedem Klicken auf die Baumelemente festgelegt werden. Das heißt, Die Prozedur durchläuft rekursiv alle übergeordneten Elemente und legt die Variablen fest:

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


Mit dem 1C-Cluster können Sie mit oder ohne Berechtigung arbeiten. Es gibt zwei Arten von Administratoren: den Cluster-Agentenadministrator und den Cluster-Administrator. Dementsprechend wurden für den korrekten Vorgang 4 weitere globale Variablen eingeführt, die die Administratoranmeldung und das Kennwort enthalten. Das heißt,Wenn im Cluster ein Administratorkonto vorhanden ist, wird ein Dialogfeld zur Eingabe der Anmeldung und des Kennworts angezeigt. Die Daten werden im Speicher gespeichert und in jeden Befehl für den entsprechenden Cluster eingefügt.

Dafür ist das Fehlerbehandlungsverfahren verantwortlich.

ErrorParcing
 proc ErrorParcing {err opt} { global cluster_user cluster_pwd agent_user agent_pwd switch -regexp -- $err { "Cluster administrator is not authenticated" { AuthorisationDialog " " .auth_win.frm_btn.btn_ok configure -command { set cluster_user [.auth_win.frm.ent_name get] set cluster_pwd [.auth_win.frm.ent_pwd get] destroy .auth_win } #RunCommand $root $par } "Central server administrator is not authenticated" { AuthorisationDialog "  " .auth_win.frm_btn.btn_ok configure -command { set agent_user [.auth_win.frm.ent_name get] set agent_pwd [.auth_win.frm.ent_pwd get] destroy .auth_win } } "   " { AuthorisationDialog " " .auth_win.frm_btn.btn_ok configure -command { set cluster_user [.auth_win.frm.ent_name get] set cluster_pwd [.auth_win.frm.ent_pwd get] destroy .auth_win } #RunCommand $root $par } "    " { AuthorisationDialog "  " .auth_win.frm_btn.btn_ok configure -command { set agent_user [.auth_win.frm.ent_name get] set agent_pwd [.auth_win.frm.ent_pwd get] destroy .auth_win } } (.+) { tk_messageBox -type ok -icon error -message "$err" } } } 


Das heißt,Abhängig davon, was der Befehl zurückgibt, wird entsprechend reagiert.

Derzeit ist die Funktionalität zu 95 Prozent implementiert, die Arbeit mit Sicherheitsprofilen und test =) muss noch implementiert werden. Das ist alles Ich entschuldige mich für die zerknitterte Erzählung.

Der Code ist traditionell hier verfügbar .

Update: Arbeit mit Sicherheitsprofilen abgeschlossen. Jetzt ist die Funktionalität zu 100% implementiert.

Update 2: Lokalisierung in Englisch und Russisch wurde hinzugefügt, Arbeit in Win7 wird überprüft
Bild

Source: https://habr.com/ru/post/de415835/


All Articles