Over the past month or so, I’ve been working on a project to provide remote access to Okuma’s THINC API. Since I’ve been working on this in my spare time, progress has been slow. Still, I started this series more than a month ago, and rather than meandering through numerous future entries, I’m going to try to bring some closure in part III.
So I’ll start at the end. Here’s the finished system…
The Client Library
The interface is exactly the same as Okuma.CLDATAPI.dll in terms of namespaces, enumerations, and class hierarchy. This was done so that an existing THINC application can be recompiled against this new library with no code changes.
Rather than hand-code all of these classes, I wrote a simple application that used Reflection to go through all of the classes and methods in the real THINC API assembly, and then automatically generate stub code for me.
Most of the THINC class methods simply call a protected method in CBase, which sends a request to my THINC server application. There were a few special cases:
CMachine.Init() displays an input box. The user must specify the hostname of the THINC CNC. Otherwise, the library does not know where to connect.
CMachine.Close() does nothing. The protocol between the client and server is HTTP, and therefore stateless, so there is really nothing that we can sensibly do.
All of the SetSubSystem() and SetDataUnit() methods simply store the given subsystem or data unit locally within the class instance. The library passes these values as necessary whenever it sends a request.
We should be able to take a THINC app, build it against this library, and then run the THINC app remotely. When the app calls CMachine.Init(), the user will be prompted to enter the location of the THINC control.
A Simple Test Application
To test this, I created what may be the simplest possible THINC app: getting and setting a common variable. When I run the application, here’s what I see:
This is the remotable client library prompting for a hostname. Since I don’t currently have access to a THINC control, I fired up my server application on my local machine, and pointed the client library to localhost.
It works. Other than that, there’s not much to say about it. I could recompile the same app against the real Okuma THINC API and run it on the control.
On the THINC Control
Like I said, I don’t currently have access to a THINC control, so I ran the server-side component on my local machine. It’s a simple console app that initializes THINC API (or in this case, my THINC simulator classes) and then listens for HTTP requests.
When a request comes in, the server attempts to interpret them in the format:
From there, the server invokes the appropriate method. If it is successful, the server will answer with an XML document containing the return value of the function. Or if the method was a subroutine, the server will answer with a 204 (No Content) response. When an error occurs, the server will respond with a 404 (Not Found) or a 500 (Server Error) response.
Here’s what the console looked like after running the remote THINC app from earlier:
You can see where I called GetCommonVariableValue and received a value of 0 (zero). Then I called SetCommonVariableValue with a value of 1.234, which is the value returned the next time I called GetCommonVariableValue.
Obviously, the one major task that I haven’t done is testing on an actual THINC control. This probably won’t happen over the holidays. I still haven’t tested how more complicated types (such as CCurrentAlarm, or the MacMan report classes) handle being serialized to XML.
On the client side, I would add additional functions so that the developer can specify the hostname programmatically, or through a .config file.
The server has a lot of rough edges that need to be smoothed out. Ultimately it’ll turn into either a tray application or a Windows service.
Since a remote app will run over the network, latency can really slow down any THINC requests. Imagine an application that sets the value of 1000 common variables. Now imagine that each SetCommonVariableValue() call takes about 50ms. Your application will be frozen for close to one minute while setting all of the common variable values. Caching would help in some cases, but it would be nice if the server were extensible enough that a developer could specify “I want to set 1000 common variables in one request.” This server doesn’t yet support that.
Methods that require local filesystem access (e.g. CProgram.LoadMainProgram) also won’t work very well. If your application allows a user to load a program by browsing the MD1 directory with an OpenFileDialog, it’s not going to work remotely.
And, of course, this has all been for the Lathe version of THINC API, Data API only. I haven’t touched the Command API, nor the Mill version.
So in summary, there’s still a lot of work to do. The prototype should convey the basic idea: with this system, we can run existing THINC applications remotely, simply by recompiling against this client library. This allows us to leverage our existing THINC codebase.