Ted,
First of all, congratulations on writing a rather complex application that has been bug free for six months. That is commendable no matter what design paradigm you use. After all, if I had a choice between inheriting well-written procedural code versus badly written OOP code, I'll choose the good code over the bad every time.
Now, to try to answer your question, one way I see that OOP could improve the design is in later code maintenance. Let's say somebody had to later take over your code, how quickly could they get up to speed? How quickly could they find the exact spot of a particular error?
Granted, you may have organized your subroutines into various modules, but in the end it still basically amounts to one large collection of hundreds of subs and functions. The new programmer will have to trace through the code, following it's logic, to slowly discover the different parts of the system. Let's say the bug the programmer is trying to find happens near the end of the application's "life" (like the end of the day). To discover which subroutine is causing the error, and to understand how it got to that state, the programmer pretty much has to step through the code, procedure call after procedure call, until he finally gets to the spot where the error is.
If everything is an object, it tends to be much quicker to find what you are looking. If the bug has something to do with one of the buses, you can be pretty confident that you should concentrate on you BUS class.
To argue from the opposite end of software development, I find it much easier to translate requirements into OOP design rather than procedural. Procedural programming forces you think sequentially, which is usually not how things happen in real life. With OOP programming, you can usually translate your Use Case statements directly into OO code, because you simply take the nouns in your Use Case and translate them into objects. For example, to take a snippet from your own Use Case:
tedsmith said:
This was a bus terminal that had 20 BUS STOPS, each with a PC (& another application) feeding 6 MONITORS, a DATABASE that contained all the TIMETABLES and a MONITORING TERMINAL that could see and change all departure times if the wrong BUS turned up and send advice messages and multimedia presentations to each stop.
Everything I've capitalized are candidates to become objects, everything I've italicized are likely methods that the object contains to do its work. For example, the class MONITORING_TERMINAL would have a method SendAdviceMessage. See how using OOP techniques, you can build code that mirrors the real life situation. It is a far more intuitive organization of code, because it is based on real life. Also, you do not have to think as abstractly as you would if you had to implement this procedurally (i.e. you don't have to constantly check in a loop if it's time to SendAdviceMessage, instead there would be some event that would trigger it, probably by some collaborative object).
I hope I've made the OOP concepts somewhat clearer. I have only really touched on the bare bones of OOP, there are other usefull concepts such as inheritance, design patterns, etc.