TNI SDK update

Thank you Chris for the update with the sample rule and library.

It pointed out what I was doing wrong so now I think I can actually make some progress in getting my code to work. :)

--Grant
 
No problems. The ball's in your hands now, so give me a yell with any progress you make or problems that you encounter.

There's obviously a lot more to be done on our side, but we'll only proceed with that once we're sure that the basics are working.

chris
 
I compiled the sample and moved the TNISample.dll in the same folder as TANE.exe, added the sample rule to a session but it doesn't seems to be working.
Is it something wrong with what I've done ?
 
I compiled the sample and moved the TNISample.dll in the same folder as TANE.exe, added the sample rule to a session but it doesn't seems to be working.
Is it something wrong with what I've done ?

I did get it to work, but it took several steps.

First, I had to make sure that the logging on the Trainz Settings -> Dev tab was enabled.

Next, I started surveyor and added the rule to a session.

Then I did Developer -> Clear Logs and Devloper -> Show Logs.

Finally I clicked edit with the Sample rule highlighted.

This is the result in the log:
Capture.jpg
 

Attachments

  • Capture.jpg
    Capture.jpg
    59.3 KB · Views: 6
Thanks, I will try this and report if it works. I think I was not looking at the right things.
The end of the doc we had last week speak about posting messages to access scripts. Chris, can you confirm this is not implemented yet?
 
It works for me, too. I also managed to attach Visual Studio debugger to the TANE process, set breakpoints and debug TNISample.cpp. All four functions were called in the expected order. Unfortunately, there is no pdb (symbol) file for the TNI classes declared in the header files. That would be quite helpful when debugging your own plugin.
 
Now working fine for me.
I also found that the sample send TrainzScript message but I don't understand exactly how.
It seems to just store the name of the fonction (here PostMessage) and then use the TNICallLibraryFonction fonction. What does exactly do this and is it possible to do this with any TrainzScript fonction ?
 
It works for me, too. I also managed to attach Visual Studio debugger to the TANE process, set breakpoints and debug TNISample.cpp. All four functions were called in the expected order. Unfortunately, there is no pdb (symbol) file for the TNI classes declared in the header files. That would be quite helpful when debugging your own plugin.

That's useful to know. Thanks. :)
 
I also found that the sample send TrainzScript message but I don't understand exactly how.
It seems to just store the name of the fonction (here PostMessage) and then use the TNICallLibraryFonction fonction. What does exactly do this and is it possible to do this with any TrainzScript fonction ?

No; it has nothing to do with TrainzScript functions as such. I've added a new "TNILibrary" section to the "TNI Development Process" google doc, which might help you understand this process better. You'll also find that the existing documentation regarding the TNITrainzScript library is probably relevant here.

chris
 
It works for me, too. I also managed to attach Visual Studio debugger to the TANE process, set breakpoints and debug TNISample.cpp. All four functions were called in the expected order. Unfortunately, there is no pdb (symbol) file for the TNI classes declared in the header files. That would be quite helpful when debugging your own plugin.

What exactly are you seeing as missing in your debug view, that you expect to get from the PDB files?

chris
 
What exactly are you seeing as missing in your debug view, that you expect to get from the PDB files?

I'd like to inspect the member variables - if there are any - of the instances of the classes derived from TNIObject which are passed to the plugin public functions. But if it's all done via some sort of "properties" pattern, i.e. interfaces, getters/setters, then that wouldn't bring any benefits, of course.

One other thing: You have designed TNI to support non-OO plugin implementations. Have you thought about any recommended pattern for the plugin developer to convert the procedural interface into an OO interface? The approach I would possibly take is do create a class that holds my implementation. Then manage it via a static std:map with the TNIContext* as the key and an instance of my class as the value. All static TNI function calls would be routed to member function calls, through a dispatcher that works with "delegates", presumably implemented as functors or lambda expressions. There are a number of options, some quick and dirty, some more elegant, and a guideline might be helpful to developers.

A minor detail: The sample introduces the usage of the platform-independent C++11 std::mutex class, but the sample explicitly calls its lock() and unlock() functions. While this is certainly fine for a "Hello World" type of application, it should not be done that way in production code. C++11 offers the the lock_guard class to handle mutexes. lock_guard implements the RAII programming idiom (an idiotic acronym and name, nobody would associate automatic release functionality with it). The C++ example in the Wikipedia article explains its features.
 
One other thing: You have designed TNI to support non-OO plugin implementations.

Yes and no. C++ doesn't make for a good plugin architecture because of the fragile base class problem. As a result, we avoid OOP in the actual API, however that doesn't mean that you can call into the TNI functions easily from other languages. It's likely that most problems could be solved by repurposing the class forward declarations to an empty struct type (for C) or similar, but we haven't specifically tested for this, nor do we have any current plans in this direction. Even if somebody made an interface module for Delphi or VB, we don't accept plugins written in other languages anyway.


Have you thought about any recommended pattern for the plugin developer to convert the procedural interface into an OO interface? The approach I would possibly take is do create a class that holds my implementation. Then manage it via a static std:map with the TNIContext* as the key and an instance of my class as the value. All static TNI function calls would be routed to member function calls, through a dispatcher that works with "delegates", presumably implemented as functors or lambda expressions. There are a number of options, some quick and dirty, some more elegant, and a guideline might be helpful to developers.

As far as I'm concerned, this is a plugin implementation detail. There are numerous ways to solve it, and it might make sense for people who have similar goals to get together and write up a common approach rather than each reimplement it. My main suggestion would be that you keep it simple; we will be code reviewing any submissions and if we think it's overly complex for what it achieves then that will likely come up as a flaw in the review. There's no problems with using a std::map<>, but if you're working with statics then be careful of startup/shutdown/thread safety.


A minor detail: The sample introduces the usage of the platform-independent C++11 std::mutex class, but the sample explicitly calls its lock() and unlock() functions. While this is certainly fine for a "Hello World" type of application, it should not be done that way in production code. C++11 offers the the lock_guard class to handle mutexes. lock_guard implements the RAII programming idiom (an idiotic acronym and name, nobody would associate automatic release functionality with it). The C++ example in the Wikipedia article explains its features.

Use whatever approach you like. RAII is a solid paradigm but it is not the only paradigm.

chris
 
Back
Top