Redge Shepherd
TclTk Scripts

TclTk Scripts

Managing MQTT Broker Clients - Part 4

Managing MQTT Broker Clients - Part 4

Using a Tcl Dictionary to Store and Retrieve MQTT Broker Client Settings Using a File

Redge Shepherd's photo
Redge Shepherd
·Jan 9, 2022·

4 min read

Subscribe to my newsletter and never miss my upcoming articles

Table of contents

  • MQTT / Broker Connections
  • Create a "Connections Configuration" File
  • Where To Save "Connections.cfg"?
  • Import Connections
  • Export Connections
  • Next Steps

Managing MQTT Broker Clients - Part 4

MQTT / Broker Connections

Our previous post, " Managing MQTT Broker Clients - Part 3 " closed with the requirement to save our connections to and retrieve them from a file.

Although an SQLite database is likely the better choice where numerous connections are concerned, I don't anticipate a requirement to manage more than a few connections. For the sake of simplicity, it is easier to read and write to a dictionary of connection names with related settings that we can save directly to a file.

We populate the dictionary by reading the file's contents to a dictionary variable. We save the dictionary by writing the dictionary's contents to the file. As such, all connection names are readily available to our application.

Create a "Connections Configuration" File

We will save and retrieve our connection information to a file named "Connections.cfg," where the ".cfg" extension is a typical abbreviation for "configure" or "configuration." Rather than hardcoding the name of our file, it is better to save it to a variable: set configFile "Connections.cfg"

We will use Tcl's "open" command to read and or write the file's contents. We create the file if it does not exist. Before writing our import and export connections, we need to know where the file is.

Where To Save "Connections.cfg"?

We want this file to be in the exact location of our application so we can easily find it. Tcl makes it easy to do just that. I typically begin my code with the following snippet:

set AppFULLNAME ""
set AppFILENAME ""
set AppPATHNAME ""
set SEPARATOR [string index [file normalize [file separator]] end]

puts "argv0 = $::argv0"
set AppFULLNAME [file normalize [info script]]
puts "AppFULLNAME = $AppFULLNAME"

# Extract filename from the full path + filename
set AppFILENAME [file tail $AppFULLNAME]
puts "AppFILENAME = $AppFILENAME"

set AppPATHNAME [list [file dirname [ file normalize $AppFULLNAME ]]]
puts "AppPATHNAME = $AppPATHNAME"

The following global variables are accessible to any procedures that require them: AppFULLNAME = The full name of the application, including path and filename. AppFILENAME = The name of the command or Tcl script. AppPATHNAME = The pathname of the command or Tcl script without the filename. SEPARATOR = The character between the volume/drive, directories, subdirectories, and filename. This character is typically a "/" or "\" as pictured below:

image.png

We can now use the pathname for our application as the pathname for our "Connections.cfg" file. All we have to do is write our Import and Export procedures.

Import Connections

Our "Connections.cfg" file must exist before using The Tcl " open " command to read it. Our import procedure checks to see if the file exists and, if not, creates the file and writes the current "connection" settings to it.

If the file "Connections.cfg" exists, we open it and read the file contents to the dictionary, ::BrokerClients. The syntax for the call to our ImportConnections procedure is: ImportConnections $configFile ::BrokerClients.

The code for ImportConnections is as follows:

proc ImportConnections { filename dictionaryName } {
    # debugging / information only.
    puts "Execute proc ImportConnections"
    puts "Parameter (filename):  $filename"
    puts "Parameter (dictionaryName):  $dictionaryName"

    # The dictionary is a global variable
    upvar #0 $dictionaryName dictionary

    # Create a full file path to the dictionary.
    set nativefile [file nativename [file normalize [file join $::AppPATHNAME $filename]]]
    puts "Import Connections nativefile FullPath:  $nativefile"

    # File MUST exist to open the file for reading.
    if { [ file exists $nativefile ] } {
        puts "File Found:  $nativefile"
        set fp [open $nativefile "r+"]
        set dictionary [read $fp]
        close $fp
    } else {
        puts "File NOT Found:  $nativefile"
        puts "Creating $nativefile"
        set fp [open $filename "w"]
        puts $fp  $dictionary
        close $fp
    }
    puts "Exiting proc ImportConnections."
}

The line containing puts $fp $dictionary is the only puts command we need to successfully create the "Connections.cfg" file. The remaining lines containing puts commands are for informational or debugging purposes only, and we can safely remove them by preceding them with a "#" to comment them out.

Export Connections

The export connections procedure is not as complicated as we only need to write the contents of our dictionary to the Connections.cfg file. We are using the Tcl " open " command with the "w" option to open the file for writing only, truncating the file if it exists, and creating the file if it doesn't.

The syntax for the call to our "ExportConnections" procedure is: ExportConnections $configFile ::BrokerClients.

The code for "ExportConnections" is as follows:

proc ExportConnections { filename dictionaryName } {
    puts "Execute proc ExportConnections."
    puts "Parameter (filename):  $filename"

    upvar #0 $dictionaryName dictionary

    set nativefile [file nativename [file normalize [file join $::AppPATHNAME $filename]]]
    puts "Export Connections nativefile FullPath:  $nativefile"

    set fp [open $nativefile "w+"]
    puts $fp $dictionary
    close $fp

    puts "Exiting proc ImportConnections."
}

The line containing puts $fp $dictionary is the only puts command we need to create or write to the "Connections.cfg" file. The remaining lines containing puts commands are for informational or debugging purposes only, and we can safely remove them by preceding them with a "#" to comment them out.

Next Steps

Now that we have procedures to import and export our settings, we can work with multiple brokers concurrently. We could continue adding more buttons to run these procedures, but it is time to add a menu to execute them instead.

Did you find this article valuable?

Support Redge Shepherd by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this