Defining callbacks to receive external commands?

Hi,

I would like to send commands to my Ruby plugin from an external source. They should be executed as soon as the command is send. While it’s possible to register callbacks for WebDialogs, as far as I know that’s not possible in other situations. Having a background thread blocks the whole program, pulling a file constantly does too.

I imagine, it could be possible to use a WebDialog to open a webpage from a local webserver which can receive commands from the external source and redirect it to the plugin via the webpage and registered callbacks in ruby.

Before I try this (in my opinion hacky) workaround, is there a simpler solution?

Can you elaborate on your source?

There are no API methods for callbacks from outside of the application. Knowing what kind of external source would help in making a suggestion - otherwise it’d be pure guessing to what might work for your particular situation.

In this case it’s another company with software written in C++. The simplest example I can think of is sending a command to highlight a particular object in Sketchup, which is selected in the other software and distinguished by a GUID.

I think I would have tried either sockets, shared memory or something like that. From a Ruby C Extension: https://github.com/SketchUp/ruby-c-extension-examples

The reason I wouldn’t use Ruby sockets is because getting Ruby threads to work in the SketchUp environment. For some reason Ruby still manage their threads and hosted within a desktop application something cause them to not run unless the main thread is busy.

Which is why I’d suggest writing a small Ruby C/C++ extension.

Yes, that’s the second option I thought of, but since I don’t know more than the absolute basics of C, I would’ve prefered the pure ruby way. Guess it’s time to dig in…

Yea, Ruby would have been easier - had it not been for the threading issues.

Ok, now I got something, but I don’t know how to proceed. After I downloaded the C extension example you linked to I created a new method MyNativeClass::Start() to start my C# server (a console application) and read the stdout pipe. This works fine, a console window opens and the output is redirected to the Ruby console inside Sketchup.


void MyNativeClass::Start() {
	char *cmd = "C:\\Code\\Server\\bin\\Debug\\net46\\win7-x64\\Server.exe";

	char buf[BUFSIZE];
	FILE *fp;

	if ((fp = _popen(cmd, "r")) == NULL) {
		ruby_interface_.CallFromNative("Error opening pipe!");
		return;
	}

	while (fgets(buf, BUFSIZE, fp) != NULL) {
		// Do whatever you want here...
		ruby_interface_.CallFromNative(buf);
	}

	if (_pclose(fp)) {
		ruby_interface_.CallFromNative("Command not found or exited with error status");
		return;
	}
}

Now do you have a idea how I can start this in a new background thread?

Something like:

cmd = "\"full_path_to_exe\""
spawn(cmd)

?

I don’t have a concrete example I’m afraid. If you plan to call SketchUp Ruby API methods they must be from the main thread. So you probably need something that polls your worker thread from the main thread (without blocking it - timer?)

Are you planing on using pure C?
You can use C++ within these Ruby C Extensions - as long as the Init function is declared as C.

So it’s not as easy as I initially thought. Don’t know if something like this is possible, if someone else has more examples, I would appreciate your help.

I’m pretty open about it, either will be almost completely new to me.

@jeroen - did you guys play with sockets?

Not really Thomas. Reading the above, I’m afraid I can’t really help.
I was trying to use named pipes to communicate between a ruby c extension in Sketchup and an an external console application. Never really tried sockets, maybe I should.
Before that I started with popen3 to launch my external app and get IO handles to communicate over. It all worked fine on mac, but it drove me nuts on windows. Seems like ruby spawn is a different beast on non posix platforms like windows. Tried alternative implementations found on github… Some worked on pure ruby on windows, non from within Sketchup/ruby.
I’m still looking for a decent solution on windows. Currently using popen3 on mac (very easy pure ruby, no issues) and a dirty file based hackish alternative on the windows version. It does the job, but it’s ugly.

Hi,

I have an extension that draws buildings. I have a toolbar button in SketchUp that launches an executable. (see code block below) I actually have a ‘launcher’ program that I use to launch the program. Otherwise SketchUp is blocked until the program closes.

I then start a timer that polls a ‘spec.txt’ file. My external program sends specifications for drawings to the spec.txt file.
I can scroll down through the list of quotes and ‘preview’ them in real time as they are drawn in SU.
When the external program closes it deletes the spec file to let SketchUp know to stop the timer.

Alternately you could set a timer to continuously poll for the file at longer intervals, and then increase the timer speed once a file is there to pull data from.

I didn’t have much luck with using a socket because of UI blocking.

Here is the code to launch the import program. (sorry I couldn’t find a button for code blocks)

def self.get_from_maestro
	Sketchup::set_status_text "Import Building From Maestro"
	path = '"' + @bcpath + 'sub programs/launcher.exe" import'
	puts `#{path}`
	if @preview_timer then UI.stop_timer @preview_timer end
	@pv = -2
	@preview_timer = UI.start_timer(0.25,true) { preview }
	sleep 2
	@pv = -1
end

And a screen clip.

Just wrap your code between lines of triple backticks
(with an optional language identifier on the 1st line):

```ruby

# code here


... comes out like:
```ruby
def self.get_from_maestro
  Sketchup::set_status_text "Import Building From Maestro"
  path = '"' + @bcpath + 'sub programs/launcher.exe" import'
  puts `#{path}`
  if @preview_timer then UI.stop_timer @preview_timer end
  @pv = -2
  @preview_timer = UI.start_timer(0.25,true) { preview }
  sleep 2
  @pv = -1
end

(But don’t use TAB chars in code for the forums. It indents way too much. Ruby uses 2 space indents.)

@DanRathbun

Thanks