Feature
Opening Doors with Distributed PowerBuilder
Opening Doors with Distributed PowerBuilder
Jun. 1, 2000 12:00 AM
Shared and remote objects - powerful features of DPB for business applications - can also be used in industrial settings. If you've been handling serial communications with many types of devices - such as gate security, pump controllers or PLCs that need to know what they're doing, or you simply want to turn your coffeemaker on remotely - read on.
That's Not a Door!
As an example I'll use a parking garage with three gate openers where you insert a card and the barrier lifts, or a red light comes on and the people behind you become very familiar. I want to locally control and monitor their operation, but I also want to do it remotely. You can buy a card reader with serial communications and a contact output to operate a door or a motorized gate that can close itself.
Each gate is on the same communication line and has its own address. To know what's going on I must continually send a status request that returns the current condition of a gate. A simple way to do this would be to have one big task handling each gate in turn. A better way is to have a separate task that runs each gate independently. The tasks will interleave for a more efficient operation. In DPB you can do this with a nonvisual object (nvo) that's created as a shared object. This creates a separate thread of execution, which is what I want. I construct an nvo, gate_opener_nvo, to monitor and control a gate. The nvo contains two functions:
- uf_configure(address): This function could read a table for gate-specific data but I'll just call the other function.
- uf_communicate(address): The object talks to the right gate opener using "address". This function uses an external global function in a DLL - gf_send_command(address, command, response). It actually handles the communication's port and protocol and I'll use it to send the command's status, "read card," "open gate" or "red light on," and "open contact" to the date- "response" is the gate's reply.
Since I have three gates, I need to create three nvos. Let's assume I read the gate addresses from a local table. In the open event of the application's main window (see Listing 1), I register and get an array of identical nvos, then call the uf_configure() for each. What happens next is the key: by POSTing uf_communicate(), a DO-LOOP that never ends, I start a separate thread, then uf_configure() returns and I start the next gate. I don't close the window since I can use it later. As long as uf_communicate() runs I have a functioning gate. It has to do something, so I construct a simple state machine in the form of a CHOOSE CASE based on the response to the status (see Listing 2). If someone puts a card in, I read and verify it against a local table of cards. If it passes, I open the gate with one command or alert the driver with a red light.
Open Sesame! ... I Think
Simple, isn't it? Pity it doesn't work. The gate threads are using the same function in a multithreaded DLL and will all speak at the same time. Since there's only one comm line, the result will be chaos. I could try and control access to the DLL with a flag that can only be held by one gate object at a time. The trouble is the shared objects can't directly see each other or a flag. The solution is to use another feature of a shared object: the serialization of function execution at the object boundary. This means the function calls form an orderly queue and the problem goes away. So within my application I can create another shared object, comm_port_nvo, and put the function, gf_send_command(), there. To make it more interesting I don't stop at this object. I create a remote shared object (RSO) in a dedicated application on the same machine, e.g., GATEWAY.exe, and have this execute gf_send_command() (see Figure 1).
I mustn't forget to build the proxy or I won't get anywhere. In the original application I create the links to this RSO. In the main window I register and get the shared object instance of comm_port_nvo before creating the gate objects. Then they can get it without error. I change the function calls in gate_opener_nvo to comm_port.send_
command(). Because the comm's function is in a remote object, I can access it from another application on the same or a remote machine. In an emergency I can bypass the gate control application and open all the gates.
Sorry, We're Full
Just as the comms have been routed through a shared object, the same can be done with the table access for driver verification. Why would I do this? Shared objects are used for common functions. Instead of each gate having a separate connection to the database, I can limit this by having the connection in a shared object, common_nvo, and the retrieval handled by a function. If I have more cards than parking spaces, I need to keep track of how many are available. I can count entrances at each gate and keep a running total. This can be done in common_nvo by incrementing a counter if the card is verified, but that keeps increasing. Now I also have to install exit gates. I'll construct another nvo similar to the gate_
opener_nvo, but this one will decrement the counter in common_nvo on card verification. When the lot fills up, everybody gets a red light. I have to change the instantiation to reflect this (see Listing 3).
Feeling Insecure?
I can also keep common data in a DataStore owned by this shared object, as well as common functions in common_nvo. As the gate objects go through their cycles, I can update the DataStore to reflect current state, last card/driver verified and date/time of last entry or exit. If I also update an access table associated with the card, by extending the verification function I can ensure that a card is used for only one entry at a time. Why stop there? I said I wanted remote access, and I can achieve this by moving the DataStore into a remote object. Now I can monitor activity by connecting to this object and having functions return the Data-Store to me. I can also add functions that allow me to change the total number of parking spaces if I need to reserve space for the mayor and his or her entourage. Remember, I can remotely open a gate to let them in.
Where Does It All End?
You may have noticed that open contact hasn't been used. I open the gate by closing a contact that acts like a momentary on switch. The gate closes itself, but I may have to open the contact for reuse since not all card reader contacts reset themselves. A safe way to do this is with a timer. I won't go into detail but it involves a C task that triggers a window event every second. This in turn decrements counters in another shared object that's shared by the gate objects. That's why I kept the window open. I know I could have handled the exit gate by modifying gate_opener_nvo, but the point is I can construct shared nvos as I add different devices for different functions. I could have a scrolling sign display "Parking Lot Full" instead of just the red light. I can add card readers equipped with a keypad and display, like some car washes. I can add more comm lines and have an array of objects of type comm_port_nvo, just like the gates. I can add parking lots, link them to a server on a WAN, then monitor and control them anywhere. Figure 2 shows a detailed diagram of what the parking lot system can look like.
Postscript
In case you're wondering, at Avocet Systems we build systems that are used to control and measure wholesale liquid distribution. They combine a standard business model with a control system that supports serial communications with multiple intelligent devices. We have to control their use, and send and retrieve data. It's what's known as a Supervisory Control and Data Acquisition or SCADA system. This involves using event and timer handlers, common data sharing, and local and WAN database access. The system is built using PB 6.5, NT 4 and Visual C++. Typically we've been using PowerBuilder for the DB business model and C++ for the control system, but a customer said, "Wouldn't it be nice if you could do it all in PowerBuilder?" So I devised the objects and methods described in the gate opener example. My objects tend to be more complex, but the basic techniques are the same. Use your imagination and see what you can do with DPB.
About John MartinJohn Martin is a senior systems architect with AssetPoint, with over 25 years of experience in the IT industry. He is responsible for Web-based and .NET product offerings.