comment 0

Soup-to-Nuts: Phonebook Module (Part 4)

Here is the final part to this module: wrapping it up and making it easy to use. SIMPL Windows is a powerful tool because it easily abstracts away a lot of details about how things happen. The way our phonebook works under the hood will be a detail we needn’t worry about, it will just plug into our larger programs.

Before we get underway wrapping up the module, let’s add one last feature I think is crucially missing from our library: alphabetizing.

Sorting Contacts

Right now, our phonebook displays contacts in whichever order they were added. That’s not typically how most phonebooks work (except for a pad of paper, I guess). It would be helpful to have them automatically sorted for us. Luckily, this is extremely easy to add to our library. We need to make a couple small changes to the PhonebookEntry class:

using System;

namespace SoupToNuts.Phonebook
{
    public class PhonebookEntry : IComparable<PhonebookEntry>
    {
        public string Name { get; set; }
        public string Number { get; set; }

        public int CompareTo(PhonebookEntry other)
        {
            if (other == null)
                return 1;

            return this.Name.CompareTo(other.Name);
        }
    }
}

By inheriting the IComparable interface, we can easily sort collections of PhonebookEntrys. Methods–like Sort–will call CompareTo to determine the order of our contacts.

Then, in our Phonebook class, we need to call Sort on our collection of contacts at key moments. I’m going to add a Sort when we Initialize:

public void Initialize(string filename)
{
    _filename = filename;
    
    _entries.Clear();

    try
    {
        using (var stream = File.OpenText(_filename))
        {
            while (!stream.EndOfStream)
            {
                var text = stream.ReadLine();

                if (text.IndexOf('|') > 0)
                {
                    var fields = text.Split('|');
                    _entries.Add(new PhonebookEntry {
                        Name = fields[0],
                        Number = fields[1]
                    });
                }
            }
        }

        _entries.Sort();

        if (OnInitialize != null) OnInitialize(1);
    }
    catch (Exception e)
    {
        ErrorLog.Error("Exception in Phonebook.Initialize: {0}",
            e.Message);

        if (OnInitialize != null) OnInitialize(0);
    }
}

This way, if we wanted to, we could upload a raw contacts file to the controller and it would automatically sort it for us when it loads. This way, we guarantee whatever is in memory is always sorted (even if it isn’t saved that way on disk). This is a huge feature we get basically for free just because we have access to SIMPL#! We also need to make sure we call Sort when adding a new entry:

public void Add(string name, string number)
{
    _entries.Add(new PhonebookEntry { Name = name, Number = number });
    _entries.Sort();

    if (PhonebookUpdated != null)
    {
        PhonebookUpdated(this, new PhonebookUpdateEventArgs {
            Index = (ushort)_entries.Count,
            Name = name,
            Number = number });
    }
}

And with that, our library is complete! It may not be the best code, it may have edge-cases that throw exceptions, and there may be better ways of solving our problem. I’m an average programmer, so I have to believe this is a typical solution. Save and Build the library, then copy the SoupToNuts.clz library over to the SIMPL Modules directory.

Phonebook Wrapper

We still need to expose a way to change SelectPageEntry from SIMPL Windows. And since this is likely the last time we’ll need to touch SIMPL+, let’s handle a little housekeeping first:

// --- Compiler Directives ---

// #CATEGORY "" 
// #DIGITAL_EXPAND 
// #ANALOG_SERIAL_EXPAND 

#DEFAULT_VOLATILE
#ENABLE_STACK_CHECKING
// #ENABLE_TRACE

#DEFINE_CONSTANT MAX_PAGE_SIZE 10

#USER_SIMPLSHARP_LIBRARY "SoupToNuts"

// --- Inputs ---

Commenting out the #ENABLE_TRACE line will prevent the Trace call in Contacts_Updated from printing anything in debugger. If we run into a problem where we need to trace into our module again, we can always uncomment #ENABLE_TRACE and see those messages again.

Add a new input to select an entry on the current page:

// --- Inputs ---

DIGITAL_INPUT Save;

STRING_INPUT  New_Contact_Name[100];
STRING_INPUT  New_Contact_Number[100];
DIGITAL_INPUT Add_New_Contact;

ANALOG_INPUT  Select;
ANALOG_INPUT  Select_Page_Entry;
DIGITAL_INPUT Remove_Selected;

ANALOG_INPUT  Current_Page;

// --- Outputs ---

And add the relevant event handler below:

THREADSAFE CHANGE Select
{
	Contacts.Selection = Select;
}

THREADSAFE CHANGE Select_Page_Entry
{
	Contacts.SelectPageEntry = Select_Page_Entry;
}

THREADSAFE PUSH Remove_Selected
{
	If (Contacts.Selection > 0)
		Contacts.Remove(Contacts.Selection);
}

I also want us to reset to the first page of the phonebook if we add or remove a contact, so update the Contacts_Updated event handler. Remember, this will cause a refresh of our page entries:

EVENTHANDLER Contacts_Updated (Phonebook sender, PhonebookUpdateEventArgs args)
{
	Trace("Contact %d updated: %s [ %s ]", args.Index, args.Name, args.Number);

	Total_Pages_Fb = Contacts.TotalPages;
	Contacts.CurrentPage = 1;
}

Save and Compile but don’t go anywhere yet!

User Macro

Modules in SIMPL Windows are referred to as user macros. Whereas SIMPL+ modules really are a blackbox with inputs and outputs, a SIMPL User Macro gets “flattened” into our program. It’s more like putting an #include into your code versus calling into a separate library or module. This can result in unexpected side-effects if you’re not careful.

Open SIMPL Windows and create a new SIMPL Module. Make sure you don’t select 2-series or XGeneration for Target Control Systems because SIMPL# is only available on 3-series or better:

You can use whichever category you want, I picked Memory

Save your empty module in the SIMPL Modules directory and name it S2N Phonebook v1.0.umc. Drop the Phonebook Wrapper v1.0 module into your program:

Our SIMPL+ module will be wrapped up inside of this module

Right off the bat, we can define our module’s interface and connect a lot of this up to our SIMPL+ wrapper:

Translating the Select_Entry_n signals to an analog value is easy enough:

Use an Analog Initialize to turn digital presses into an analog value

Page navigation may have made more sense to handle in SIMPL+, but let’s handle it in SIMPL Windows. First_Page and Last_Page aren’t too bad:

An Analog Buffer can be used to easily set Current_Page to Total_Pages

Previous_Page and Next_Page can be done also, but with a little buffering:

This Analog Increment will limit us to 999 pages

Because we have to enter parameters into the Analog Increment symbol, we’re capping our module to 999 pages. We could make this a module parameter if we wanted the option to control it, but I think almost 1000 pages will serve us well for a long time.

All that’s left is to comment out the Select and Select_Fb signals on our wrapper and we have a completed module:

Save this module, then copy all the necessary files over to your Demo Programs directory.

Phonebook Demo

In the Phonebook Demo v1.0.smw program, delete the standalone wrapper symbol and drop in our new S2N Phonebook v1.0 module. Fill in the appropriate signal names:

Next, add an XPanel to your program and wire up the signals to that:

Save and compile this program then load to your processor. Either create an XPanel or you can use the one available in my GitHub repo. Connect up and you should be able to add new contacts, delete, page around, etc:

That’s it for this module! It can definitely be improved on, so please let me know what bugs you fix or improvements you make. Thanks for reading!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s