Jump to content
  • entries
  • comments
  • views

About this blog

Notes from a U.S. Distributor / Build Shop / System Integrator

Entries in this blog

Phil Salkie

I just programmed a timed filtration system, and was very pleased at how simple the code is to handle a simple on/off timer with selectable day-of-the week:


Pretty neat, huh? I coupled that with a simple screen letting the user set start and stop times and select the days to run:


and the lion's share of the job was done.

But there's much more to Unitronics timekeeping than just simple timers. In the forums, there's been some discussion of the difference between RTC and UTC, the use of UTC to do things such as determine long periods of elapsed time, or to store and retrieve date/time values in log files. Let's try to clear up the confusion, shall we?

First, let's look at the different ways that times can be stored in a Unitronics PLC. For starters, there's RTC (Real Time Clock) - that's a collection of five MI registers, in a specific order:

Base + 0: Seconds

Base + 1: Hours and Minutes

Base + 2: Days and Months

Base + 3: Year

Base + 4: Day Of Week

If you point a function block which uses an RTC value at the special register SI30, you'll get the PLC's built-in Real Time Clock value which should be set for the current date and time of day. You can also point these function blocks at any other group of registers, so that you can manipulate time values which are not the current time, but may instead be values which the user has entered, or values stored in a table, or values which the program has saved for some reason.

Now, the main reason to use an RTC value is that it is very familiar - there's not much question in our minds about what the "hours" field means, or what "month" it is. There are several problems with this representation, though - while an RTC date is easy for a human to read, it can be a lot of work for a computer to do calculations solely on RTC values. For example, if we want to find out the difference between two RTC dates, we might find that the month has changed and that the later date has a lower day value than the earlier date, and we wind up having to compare and subtract both days and months - but months have different numbers of days depending on the month _and_ the year (that annoying leap year thing!)

Here's where the UTC value comes in. UTC is a standardized time format which counts the number of seconds elapsed since an "Epoch" value. The Vision series uses an Epoch of Midnight, January 1, 1900 as its starting point, and counts seconds in a 32 bit register moving forward from then. So, for example, 1 AM, January 1, 1900 would have a UTC value of 3600 - 60 minutes of 60 seconds each from the epoch. There are function blocks to convert from RTC register blocks to UTC values, and back again - thus, if you wanted to compare two RTC dates, you would simply convert both values to UTC and subtract the results. That would give you a difference between the dates in seconds, which you could convert to hours, days, or whatever just by dividing by the correct constant (3600 seconds in an hour, 86400 seconds in a day, etc.) It is important to realize that any given time/date value maps one-to-one with a UTC value - thus, 4:14:35 30 March 2004 equals 3,289,608,875 and 3,481,706,253 converts to 12:37:33 1 May 2010. This lets us readily do mathematics and comparisons in the otherwise complicated realm of date handling.

When would we want to actually do this? How about product packaging - I want to tell an ink-jet printer to print an expiration date which is 28 days from today. Doing this on other PLCs can be a nightmare, requiring a stored table of the days per month plus a special rung to add a day on leap years. On the Unitronics, however, we merely convert the system RTC to a UTC value, add 24192 (28 * 24 * 60 * 60) and convert back from UTC to an RTC at a different location. That RTC value then gets formatted and sent to the printer, and we're done - no worrying about rolling over the month or how many days there are in April.

Or maybe we want to time a long value - something long enough that trying to count seconds or minutes with an MI register starts producing inaccurate results due to the fact that the program takes time to scan, and every time you add a pulse, you will be off by a few milliseconds. So all you do is convert RTC to UTC in a "Now" DW register, save that value to a "Start Time" DW register at the beginning of your timing interval, and calculate your elapsed time by subtracting "Start Time" from "Now" - the result will be a doubleword integer value of elapsed seconds, accurate to durations of years. (Note that if the user changes the Real Time Clock setting while you're timing, the time result will be wrong.)

There's more to all of this - things like timer values and date strings in ASCII, but I'll cover some of that in my next post...

Phil Salkie

If you were to look over my shoulder while I'm working at my desktop or laptop PC, you'd probably notice pretty quickly that my computers don't look quite like most. Firstly, my keyboard doesn't have its letters in the usual "QWERTY" layout - I use the Dvorak layout, optimized for faster typing with minimum finger movement. I credit that (along with the refusal to use a trackpad) with curing my carpal tunnel syndrome - it took a month or so to switch over, but I've never regretted it. There's lots of information (and stickers to put on your keys) available online - you can start here.

The next thing you might realize is that my machines aren't running Windows - they've all got Kubuntu Linux installed. This gives me a fast, stable, multi-user development platform with multiple virtual desktops (my machines all have six screens' worth of display space, and I can switch between the screens by dragging the mouse or clicking on a "pager" - I don't have to minimize or hide windows to change between applications, they just stay open on different virtual screens.) I have a huge selection of free software available for me to do my work with - multiple different office suites, CAD programs, PC Board design systems, language compilers, and so on. I don't have to worry about computer viruses, trojans, malicious websites - none of those things have any real effect on a Linux system. My systems get rebooted once or twice a year - the time and money I save Howman by not buying virus software, not scanning and cleaning, not looking high and low for software drivers, and not rebooting is significant. The annoyance I save myself is priceless.

There are, however, some software packages which are only available on Windows systems - and VisiLogic is one of them. So how do I manage to do my Unitronics development while running on Linux? I run Win XP under Oracle's amazing VirtualBox system.

VirtalBox is capable of running on Linux, Intel Mac, or Windows, and can install and boot a native Win XP, Linux, Unix session from a virtual hard drive - just a special file on your workstation's hard drive. This has a number of benefits over running XP as your main operating system:

*** Since it's not your main method of accessing the internet, you're not browsing or running Flash or accessing PDFs under Windows, and Windows doesn't need to directly access USB thumb drives. That all means that Windows is much less likely to get a virus or trojan - and if it does:

*** Your virtal hard drives can be copied and backed-up like any other file - that means that if you need to install some program which may cause problems, you can just make a copy of your virtual drive and do the install - if there's trouble, just restore that backup and you're right back where you started. (That's also a great way to recover from a virus - just restore the backup hard-drive file, and you're good to go.)

*** Your XP session is on one virtual screen, but you have as many other screens as you need - so while I have VisiLogic open on one screen, I can have another screen with spreadsheets, another with CAD files, and so on - just drag the mouse to switch between them. You can even have Linux windows overlaying the Windows window, so you can read from your documentation while typing into the Windows session underneath.

*** If XP crashes (which it will, sooner or later), it doesn't affect the rest of the system.

*** Your virtual hard drive is portable between computers - if you change motherboards or move to another computer, just copy the virtual hard drive file, and you have your XP session all set up. (Make sure you have the proper Microsoft license files if you want to run the same hard drive on more than one machine!)

There are two versions of VirtualBox available - one is the Open Source version, VirtualBox OSE, and the other is the commercial version. The major difference is that the commercial version has better screen handling and handles USB ports - that lets you use a USB to Serial converter to talk to your Unitronics. The commercial version has a free trial, but it's well worth the $50 US price tag.

So, if you're a Linux or Mac user, give VirtualBox a try. If you're a Windows user and want to try out Linux, I recommend Ubuntu Linux - if you need some help getting started, drop me a line, I'll be happy to assist.

Phil Salkie

Feelin' a little Loopy!

When Programmable Logic Controllers were first introduced, their programs were geared toward the replacement of simple relay circuits. Logic statement 0 was processed first, then logic statement 1, and so on until an END instruction was reached. Then the PLC's supervisory software, or "Operating System", would do some housekeeping - update timers and counters, send outputs to the physical devices, read inputs from the physical devices, and run the user's stored program again. Logic statement five was never skipped, statement ten was never executed twice in a row.

The addition of branch control instructions changed all that. Now all but the least expensive "Smart Relay" controllers have the ability for the program to decide that rather than go forward to the next instruction, the system should instead branch backward - to make a loop. Obviously, if that loop were to go on forever, the controller would never get to execute its housekeeping after the program's "END" instruction - so we have to be careful to limit how many loops we make. This power to make a loop, combined with the ability for the controller to do something slightly different each time through the loop, gives us the power to replace hundreds, even thousands of PLC instructions with a very small amount of PLC code - and to improve the program's reliability while we're at it.

Let's take as an example the password scanning logic I wrote about last week. In that example program, I had eight nearly-identical rungs of logic - one to check the password for level one, one to check the password for level two, and so on. I could have, instead, written a small loop of program which counted from one to eight, checking the password that corresponded to that loop counter, and after the eighth time through, stopped looping and continued on to the rest of the program.

The benefits to writing loops include using less program memory space, taking less of your time to enter than copying, pasting, and changing all those rungs, and decreasing the number of typos - if you process fifteen things in a loop, either they all work or they all don't. If you write them out longhand, then you may make a typo on number 12 and not notice it for years.

The downside to writing loops is that the code is never obvious - PLCs tend not to have very clear methods of handling the kind of indirect addressing that loops use, and no two controller brands have the same indexing method. Also, it takes some setup and thought to get a loop working right - it is often easier to just cut and paste repeated rungs if there are only going to be two or three of them. (In this case, eight was right about at my limit - had it been twelve, I would have made it a loop right away.)

So now that we know some of the rewards and pitfalls, let's recode those password checking rungs into a loop structure:

First, we'll need to initialize a count variable for the loop. We'll start with MI 280 at zero, counting up and stopping when we reach eight. We then place a LABEL instruction, which is where the loop will jump back up to (PassLoop).


Next, we take the value of MI 280 and use it as an _index_ into the list of passwords, pulling out the password for the current level and storing it in MI 281.


We'll then have a single rung which checks passwords, just like the eight rungs we had before - the difference is that our new single rung will be processed eight times in a single scan.


Finally, we increment the loop counter and go back to the top of the

loop if the counter is less than 8.


And that's it! We've cut the number of rungs in half, and once it's running, we're sure that if one password level works, they all do. So, next time you find yourself starting to copy and paste rungs, decide if you're feeling loopy instead!

This is a downloadable link to a V570 application file containing the framework code with looped password checking - it has been released into the public domain, feel free to use it as a starting-point for making your own V570 projects.

To learn more about Howman Controls, visit our web page at www.howman.com

Phil Salkie

One of the requirements I frequently find in customer specifications is that Operator Terminals shall have eight hierarchical levels of password protection. The idea there is that an operator, for example, enters the level 1 password to do operator things, and a supervisor can enter a level 2 password to do anything the operator can do, plus supervisor stuff. I have yet to actually see an application where all eight levels were used for anything, but somebody somewhere once decided that eight would be the number, and it's now engraved in stone.

So, when I started using the V570, one of the first things I did was to come up with a way to implement those eight levels of password, In today's blog entry I'll walk through creating and implementing a multi-level password system, and throw in automatic screen-blanking and a password-protected "Info Mode" for good measure.

I always like to start by defining as many PLC variables as I can, so that as I program I can immediately tell if I've made a typo as I enter operand addresses. So, let's define eight MIs to store the passwords for level 1 through 8 (we'll use MI271 through 278) plus another for the user to enter their password into (MI270). (I usually set the passwords permanently using "Power Up Values" in the Operands table - you could also permanently set only the level 8 password, then make a level 8 password protected page to let the user change the values for the first 7 levels.) We'll define eight MBs for the password status - is level 1 enabled, or level 2 enabled, and so on, at MB271 through 278, plus a "Process Password" bit at MB270 and a "Clear Password" bit at MB279. As it happens, the password status bits will actually be logically inverted - Level 2's bit will be OFF if the level 2 password has been entered (as will Level 1's bit.) The reason for this has to do with the way screen items are controlled, which we'll see a bit later.

VisiLogic supports a blind entry field, which allows the user to type in a password that is not shown on the screen - that was obviously built with password entry in mind, so that's what we'll use. Here's a screen with a place to enter the password, a multi-text to show which level of password is currently active, and a button to cancel the password level. (We will put that same multi-text on our menu screen, to show what level is entered, and if no password has been entered yet, to prompt the user to put in a password.)


Now, if we have a display component we want to password protect, there are two different ways we might protect it. The most obvious thing is to just make it disappear from the screen if the entered password is not sufficient. That's what the "Hide" variable does for us - we put the MB corresponding to the desired level into the "Hide" field, and if the user has not entered a password at or above that level, the display item will just not show up on the screen. Since the control variable is "Hide" rather than "Show", we make our password bits normally ON, and turn them OFF when the password gets entered.


That's great for hiding a menu button that takes us to some restricted configuration page, but what about when I want to let a lower-level user see a value, but not be able to change it without entering a password? That's where the "Disable view" variable comes in. A variable whose "Disable view" bit is on will still show up, but it will be shown in the "Disable view" color (gray by default.) Once the proper password is put in, the variable will then show in its selected color (I usually use blue for variables which can be set by the user) and will be active to touch.


Now that we've figured out how our screens will work, let's look at the associated ladder code. Here's the first part of a series of rungs which check the entered password against the various stored passwords, and determines which level has been entered (if any):


Here we convert that number to a pattern of eight bits for the various levels:


And on this rung, we put in a timeout so that if the password has been entered for more than a preset time, we clear it back out:


As I mentioned above, it would also be nice to prevent the user from accidentally switching to the Info Mode password screen (that's caused by touching a blank area the screen for a few seconds.) We can disable Info Mode by putting a Zero into SI 50 - but we'll want to be able to get into Info Mode somehow, so we can re-enable it when a level eight password is entered. I've also put in a 10 second "back door" timer so that Info Mode is enabled for a short time just after power-up - that way, if something goes wrong with the passwords, I can still get into Info Mode:


And, lastly, here's a rung which turns off the backlight if the screen hasn't been touched for five minutes - that's less important on the LED backlight than on the CFL, but it's still not a bad idea to turn it off when not in use - I also re-enable it if an unacknowledged alarm is present (indicated here by MB 197):


This is a downloadable link to a V570 application file containing all these items - it has been released into the public domain, feel free to use it as a starting-point for making your own V570 projects.

To learn more about Howman Controls, visit our web page at www.howman.com

Phil Salkie

So I'm easing my way back into VisiLogic land, in-between phone calls from TV trucks. The other morning I get a call from one of our techs on a startup somewhere in Texas, telling me that the customer is requesting that all the panels on the "A" side of the facility have their primary power feeds fed from the "A" side breaker panels, and that on the "B" side, "A" side breakers should go to the secondary power inputs. For this they wake me up? Just swap the feeds on the "B" side, right?

Unfortunately, when we laid out these panels, we made the decision to label the primary power feeds as "Power Feed A" and the secondary feeds as "Power Feed B". So, now that the customer is hooking up power, they think of "A" as "Side A", not "Primary", and half the panels are wired up backwards, because the panels on the "B" side need to have "Power Feed B" be primary, not secondary.

So, we offer to relabel things "Primary" and "Secondary", but the customer really likes "A" and "B" because it matches the names of the sides of the building. OK, we rewire half the panels to make "B" be primary - problem solved, right?

Not so fast. It turns out that "Using Power B" is one of the alarm texts - on half the panels, it'll have to be changed to "Using Power A". Similarly, the input designation will have to change on the signal telling us if the panel has lost its primary power feed. So, I figure I'll just have the PLC program look at the network address - if the panel's on side "B", the ladder can just swap the text of the alarm. Except that I can't - the texts are stored in String Libraries, which are in Flash memory and are not changeable by the PLC program.

OK, says my technician, just make a separate version for the "A" side and the "B" side...

And, yes, I could have done that. It would have been easier and quicker - but it wouldn't have been the right thing to do. Splitting software into multiple working versions - referred to as "Forking" a piece of software - is something to avoid if at all possible. If you have an "A" and "B" side program, which are mostly the same, then in the future you've got to be sure that any changes you make to "A" are also made to "B". You also have to be sure that if a PLC needs to be replaced or have its software reloaded, that the correct version gets put in. It means that if the customer wants to have a spare on the shelf, they now either need two spares, or they have to be concerned about keeping SD cards with the different versions on hand. Forking is one of those things that seems quick and easy, and winds up causing you massive headaches a couple years down the road.

So, I thought of a couple of ways to get around the problem - using two different alarm numbers in the controller depending on what side it was on, then swapping the numbers back before passing the alarm list off to the master PLC, or putting the alarm list into the string table twice, one starting at zero, and one at five hundred, and having the list display routine add five hundred to the alarm number if the controller was on the "B" side. It all sounded like just too much effort, and I was about to hold my nose and save off an "A" and "B" version of the software, when I realised:

String Libraries aren't just for multiple languages anymore.

Each index in a String Library table has multiple entries associated with it, and by far the most common use for these entries is to support easy switching between languages. If, for instance, instead of just placing the text "OPEN" on the screen, you use "String From Library", you can have "OPEN", "ABIERTO", and "OFFEN" stored at one index. Then with a single function block, the PLC can select whether to use the first, second, or third String Library, and the text on the screen will switch from English to Spanish or German. Carry that through the entire HMI program, and you can easily have a program where the operator can set the display's language with one keypress.

Now, here's the fun part - I wasn't using multiple languages, but I _did_ need to have a handful of String Library entries change from "A" to "B" or vice versa, depending on the network address. So, I used .csv import to load the exact same list into the first and second Library, changed the "A" and "B" on the alarms and input names in Library 2, and set up a rung of PLC code to switch to either Library 1 or Library 2 depending on network address.


Boy, did _that_ make a mess! The HMI became unresponsive, the screen flickered constantly - I knew I'd done _something_ wrong, but what? Aha! I had made MB219 be "This Is Side B", then used a normally closed MB219 to set String Library 1, and a normally open MB219 to set String Library 2. However, the PLC redraws the entire HMI page every time the String Library is selected, even if it's the same library as it

was last scan. So, I quickly added a normally open SB2 to each rung so that the Library is only selected at power-up - I thought about using transitional contacts [P] and [N] on the MB219, but felt it would be better to use the SB2 and be sure that the proper library gets selected each time the PLC gets powered up.


That worked perfectly, and I now have just one software version for both sides of the plant, plus I have an extra tool in the toolbox - using multiple String Libraries to change a PLC's configuration. You could use network address, a password protected setting in a configuration screen, or even a hardware jumper on a spare input (a method that protects against accidental mis-configurations) - just have some way to let the PLC know which way to set its String Libraries, and you may be able to avoid having multiple versions of the same PLC program kicking around, waiting to trip you up five years in the future.

Phil Salkie

It's been a couple of weeks since I've run VisiLogic - that's a bit unusual for me lately, since I generally don't go a day without doing something or other on a Unitronics system. The reason is that I've been spending my time on two of the nation's newest mobile HDTV production trucks, installing control systems which combine an embedded Linux computer with networked frames of Mitsubishi PLCs and a cross-platform PC GUI written in Perl/Tk.

Working as a controls systems integrator has its benefits - a fair amount of travel, exposure to lots of different industries, the ability to pick up the occasional cool story - when I meet someone new, one of the most common questions I hear is "Controls Engineer, huh? How'd you get that job?" The answer I give is, I admit, a bit cryptic: "I was a Television Engineer for ABC Networks in New York." That leads into its own story, which I'll try not to make too boring (and I'll bring it back to Unitronics territory at the end):

You've all seen a TV Camera with a red light on the front, right? Well, did you ever stop to think how that red light knows when to turn on? Back in 1985, our engineering group at ABC was presented with a problem - we were installing a new little TV studio which would allow us to feed different programming and commercials to different sections of the nationwide ABC network. This meant that for the first time, more than one studio could be "On The Air" to network at once, and the red lights on dozens of cameras and hundreds of TV monitors would need to react accordingly. (Today, that's one _small_ truck worth of gear...)

The technology we used to turn those red lights on was bank after bank of slide-matrix switches - all set up by hand for each show, and all physically wired to the various devices, which all had to have compatible voltage levels (or have frames of interposing relays). Our quick calculation of the number of those switches required to get the red lights to do what we now needed them to do pointed out that the matrix switches would take up all the room we had reserved for the actual TV monitors, plus another rack or two. Time to look for another answer, as finding a bigger room wasn't an option.

One of our engineers happened to be looking through a trade magazine and saw a new device from Mitsubishi - a "Programmable Logic Controller", or "PLC". It could have hundreds of inputs, and hundreds of outputs, and had a CPU which could read in the inputs and control the outputs. This would allow us to replace seven or eight racks full of matrix switches with a frame which could mount under a console, and be controlled by that new thing which I was one of the department experts on - a "Personal Computer" (this was just before that mainframe company got into the PC business.) We could also save patterns to floppy disk and load them back up, saving many hours of setting up and checking out the tally light configurations.

Well, the software turned out to be much more of a project than we had envisioned, since we were trying to emulate a matrix switch with tens of thousands of crosspoints - more crosspoints than the controller had memory to store. I tried saving a compressed matrix pattern as data tables in the controller's memory, only to find that it took so long to scan the data tables that the red lights would only update every few seconds - far too slow for the precise timing of television production. The eventual method I used was to reverse-engineer the language used to program the Mitsubishi controller, and have the personal computer software write its own PLC program and download it to the controller - something the manufacturer didn't support, and wasn't sure would even work. But work it certainly did - that system was the first of a whole series of PLC installations at ABC networks, and PLCs were deemed by the engineering department to be the most reliable equipment in the entire plant.

Eventually I moved over to work for the distributor we had bought the controllers from, and who had helped us get un-released components and untranslated documentation via air cargo from Japan in order to make a PLC do something that was considered impossible. That distributor was Howman Controls, and twenty plus years later, I still work here, and we still do some off-the-wall projects, and we still make Tally systems for the broadcast industry using Mitsubishi controllers.

However, this latest installation has got me thinking - what if I were to use a V570 system instead? Although the PLC frames don't currently have any operator interface device at all, if we used a Unitronics I could provide a diagnostic display allowing for easier installation, testing and troubleshooting. TV trucks take an amazing pounding as they drive around from venue to venue, and it's not unusual for interconnecting wiring to fail, or connectors to become dislodged - having a readily accessible display would be a great feature, and given the difference in pricing between the two PLCs, it's essentially a free feature.

PLC to PLC networking is also a built-in on the V570, so that would now be standard instead of an extra-cost item. The faster processor and native data-table handling bypasses the need for the reverse-engineered PLC programming trickery I've used in the past, and the availability of ethernet and RS-422 communications would let the Unitronics communicate with the various routing, control, and display devices on the truck, potentially giving smaller installations the same power as our current Linux/PLC hybrid top-of-the-line systems. I can see there's going to be quite a bit of R&D work in my future...

Phil Salkie


I'm Phil Salkie, Lead Engineer for Howman Controls in Edison, NJ, USA ( http://www.howman.com ). We're a small distributor and manufacturer of industrial controls products, and a UL-508A panel shop. Howman has been in business for about 30 years, and I've been here for 25 of them - if you've watched a sporting event on TV, dealt with a large bank, had a wound sewn up, or bought a banana, a gallon of gasoline, or a computer chip, there's a good chance that some of our hardware or software was in the mix somewhere in making that item or service, or getting it delivered safely to you.

We've been a Unitronics Distributor since around November 2009, and we've got about 15 projects (mostly based on the V570) in the field now, some with just one controller, some with a dozen - I'm starting to feel like I've learned some things, and that some of what I've learned could be interesting enough to share.

Perhaps the first thing I learned was just how powerful the Enhanced Vision's communications functions are. My first project, when we were evaluating Unitronics as a possible replacement for an Idec Open-Net PLC, was to write a communications driver which let the V570 talk over an RS-485 network to an Idec master. I was able to implement a completely functional network slave in a surprisingly small amount of ladder code and an equally surprisingly small amount of my time. Since our development cycles are mind-numbingly short, I was quite impressed by how quickly it came together, and we signed on with Unitronics soon thereafter. In subsequent projects I've written other drivers, and found that some protocols don't happen to fit well with the features in the comms function blocks - but there's always been a way to make it work, and so far it's been pretty straightforward to implement communications to foreign devices. We now regularly install V570s which talk on two networks to two redundant Open-Net masters - something the Open-Net itself can't do.

Something that I found fairly quickly after the first discovery was that Unitronics has an extremely responsive support group. We are definitely "power users" here - we tend to push hardware and software right to its limits immediately after figuring out how to turn the stuff on. Our Idec network driver uncovered a problem with an intermediate firmware upgrade, and (unlike any other PLC vendor we'd ever worked with) the folks at Unitronics took the demonstration program we gave them, diagnosed the problem, and issued a firmware upgrade to fix it, all within days. We've asked for features, and found things in VisiLogic that needed work - and while not everything gets done overnight, it has been a pleasant surprise to see how many of the things we've pointed out have been addressed, and how quickly that work has gotten done. I won't drag out the war stories of trying to get some of those Other vendors to fix problems with their products, I'm sure we all have our share of those!

One tool which I've found indispensable, and which I'll recommend to you, is AutoHotKey ( http://www.autohotkey.com ). It is a free, open source Windows keyboard/mouse macro editing language and tool set which lets you automate some of the little mind-numbing tasks we encounter. When you want to change the color of fifty little on-screen items, you can just program Control-Shift-O to click on the item you've pointed at, select the color palette, pick the new color, click OK, and close the edit dialog box. A tremendous time saver for any Windows program where you have to do the same thing again and again and again...