Interest in a Networking Sockets Workaround
-
It seems that Sketchup is running the bridge_load script as I can get UI.messagebox statements that I put into it.
I even get the "SketchUp Bridge already started", if I force RDE to run the macro via Macro->SUB. So it seems like something is getting executed.
But when I go to use Sketchup.version in RDE I get C:/Users/RABIDC~1/AppData/Local/Temp/rb4126.tmp:1:in `<main>': uninitialized constant Object::Sketchup (NameError)
Which makes me think it's not being recognized.I know I could have all sorts of configuration problems so let me know what you think. Ruby 192, windows 7, Sketchup 8.
-
@rabidcicada said:
Hey Dan,
Your example ruby code for the Dictspy.rb doesn't do any good without the bridge part.Of course not! (I was showing what needs to be done on the Ruby-side.)
YOUR bridge needs to write "transactions" (which will be attributes consisting of a key [a unique timestamp is fine,] and a value which would be your data [as described in the first post,]) to the attribute dictionary of a special name.
I chose the dictionary name 'SocketQueue' for the example.
On the C++ side, you'll need to get the handle to the current model, and write your messages to the attribute dictionary named 'SocketQueue', using it's set_attribute() method.
You don't have a choice. The only way to have Sketchup perform an event-driven task, (because Sketchup "owns" the event loop,) is to use one of the Ruby API's observer classes that reacts to changes in a list-like object. The only candidate is the the AttributeDictionary class for the queue (which is a subclass of Entity,) and a custom EntityObserver subclass.
Then to process messages you (or whomever uses the bridge,) needs to replace the comment:
# This is where the message processing conditional statement goes.
(within the process_message method,) with some kind of conditional statement block(s) (if or case, etc.) that processes the messages.In practice it may be easier if coders create a subclass of the DictSpy, and override the process_message() method. The name DictSpy could be changed to QueueSpy, SocketQueueSpy, etc. whatever you wish.
You can get fancy and add a module method that dettachs the observer to stop processing, and another to attach it again. Perhap some queue methods to clear the queue, etc.
-
@rabidcicada said:
I know I could have all sorts of configuration problems so let me know what you think. Ruby 192, windows 7, Sketchup 8.
Sketchup 8 loads Ruby v1.8.6-p287
Sketchup cannot load Ruby in the 1.9.x branch (as far as I know, unless you found some trick?)RDE could be the problem... try testing your code using Alex's WebConsole/Editor.
-
RabidCicada how do you run the code in RDE ? from that error it looks that you dont use sub.exe to send the code from RDE to SketchUp via SketchUp Bridge
-
Thanks for the responses.
I am SEVERELY unfamiliar with hacking up sketchup...FYI.
Question:
Does Sketchup have it's own local copy of Ruby 1.8.6 etc? Or are you saying that I must use that version if I am to successfuly drive sketchup externally?I run code through RDE by simply typing it into the main editing window and then selecting "run" from the menu. Is that not the right way?
I've already registered the SUB script with RDE.
I may not be "executing" ruby code the right way. I start Sketchup with the scripts and exe in the plugins directory. I believe bridge_load.rb runs because I get a messagebox notification I put in there on purpose (for the success case). Then I start RDE. I run SUB script via toolbar->macro->SUB (I get the error about only one instance being allowed). Then I type some code into the normal box in RDE. Then I hit "run" and I get the error.Am I supposed to do things differently?
I would appreciate if you laid out an exact step by step example to successfully execute a "Hello World" in a messagebox from the RDE interface.
-
Should I separately install Ruby 1.8.6?
-
Thanks for the pointer to Alexs stuff...I'll play with that also.
-
Oh...Wait a minute....
I'm at work so I can't test this but.....
Are you supposed to (For each and every piece of Ruby code you want to execute):
(1)Put your code in normal RDE console
(2)Execute SUB script via Toolbar->Macro->SUBI assumed that the bridge was a constant bridge and that you just execute it once....then input and "run" ruby code as normal in RDE.
-
@unknownuser said:
(1)Put your code in normal RDE console
(2)Execute SUB script via Toolbar->Macro->SUByup, that is the workflow to use SketchUp Bridge. there is no need to install separately Ruby 1.8.6, but it helps in developing (checking source, running quick tests outside Sketchup in an automated way, ...)
-
Thanks TBD.
I just didn't pay close enough attention to your instructions...which clearly state what to do.
-
back to the original post... since there's seems to be some interest in networking sockets for sketchup. I'll tell you my findings so far...
While, I would also be interested in a C++ networking bridge, I have had success using ruby sockets in sketchup 8 on windows 7. This might be easier for people who only know ruby. Ruby sockect class comes from the socket.so file found in the 1.8.6 full ruby install. Once you have that file, just require it in your code:
require 'socket'
as you can probably guess I copied the socket.so file to the /plugins directory. That seems to work fine.
For my example there are two ruby apps. One will run in the ruby console (outside of sketchup) I'll call it the "server" and the other is sketchup itself, I'll call that the "client".
Next, I initialize a UDPsocket object and bind it to an ip and port. For my purposes I am using UDP instead of TCP because I need to send lots of little messages very often and I don't care if one gets lost. If you needed to do TCP with ruby, it can do that too.
socket = UDPSocket.new socket.bind(ip, port)
"ip" is your localhost ip address, "localhost" will work if it's just local communication. and "port" can be whatever port you want as long as it's available. I use 2000 for the server app and I use 0 for the client which causes it to generate a dynamic port number.
On the server side, I setup a loop to check for incoming messages, send any new messages, and repeat:
while connection == true begin message = socket.recvfrom_nonblock(maxlen) # messages look like this; # ["message string", ["AF_INET", 4913, "localhost", "127.0.0.1"]] # the second item of the array is the address information of the sender client_ip = message[1][3] client_port = message[1][1] new_message = true rescue Errno;;EWOULDBLOCK # fires if there is no new message new_message = false rescue Errno;;ECONNRESET # fires if something goes wrong on the client side. new_message = false connection = false break end if new_message == true # do something here with the incoming message and send a reply socket.send("got it, thanks.", 0, client_ip, client_port) end end
As you can see, I had to do some error catching on the receive. This is the only way I've figured out how to get non-blocking to work. I also left out the "do something" part to keep it simple.
On the client/Sketchup side, I make a similar loop but this time I have to use a UI.start_timer instead of a while statement because I need to give control back to sketchup each loop so that sketchup can keep doing it's thing:
timer_id = UI.start_timer(0.1, true) { begin message = socket.recvfrom_nonblock(maxlen) new_message = true rescue Errno;;EWOULDBLOCK # fires if there is no new message new_message = false rescue Errno;;ECONNRESET # fires if something goes wrong on the server side. new_message = false UI.stop_timer @timer_id break end if new_message == true # do something here with the incoming message end if outgoing_message != nil socket.send(outgoing message, 0, server_ip, server_port) end }
Couple notes here. One, I have the timer set to 1/10th of a second. Haven't really tested other speeds but this seems to work pretty good. Two, "outgoing_message" for me is something created by observers. Three, I just hard code in the server_ip, and server_port("127.0.0.1", 2000). this won't change in my application.
In this example I am sending useful information to the server, not the other way. you can reverse it or go both ways. I have also left out any acknowledgment systems you might need to confirm the other side got what you sent. I believe TCP has this built in. UDP over the internet is unreliable but if it's just meant to be local, the packet gets there pretty much every time.
Anyway, just thought I would share. I'm sure there are probably some benefits to a C++ solution but I only know ruby so I'm stuck working with what I've got. If anyone looks at this and thinks this it completely wrong, let me know. I'm still learning.
-Mike
-
One last question....Did you figure out the method of ruby integration through some documentation? or did you simply poke around enough?
I guess if you are already familiar with he inner working of Ruby at the object file level then you would already recognize some stuff you'd find in Sketchups directories.
I was learning the sketchup API as a reason to learn Ruby >.<.
Anyways...thanks a lot for the treasure trove of information you've just presented me...now to do something useful.
-
@rabidcicada said:
One last question....Did you figure out the method of ruby integration through some documentation? or did you simply poke around enough?
The old "Pick-Axe" book has a chapter on how applications embed Ruby as a scripting extension. See the chapter Extending Ruby, and scroll down to the section "Embedding a Ruby Interpreter".
However, that example seems to use a standard Ruby install... Sketchup did a few custom things.. so yes a bit of poking around, trial and error, was needed to understand.@rabidcicada said:
I guess if you are already familiar with the inner working of Ruby at the object file level then you would already recognize some stuff you'd find in Sketchups directories.
Not really. Most people who learn standard Ruby first, are not familar with a Ruby implemented for embedding in applications. They expect the Ruby dirs and files to be in the standard locations.
And then Sketchup distro's with only the Core classes, and none of the standard fare of extended libraries.@rabidcicada said:
I was learning the sketchup API as a reason to learn Ruby
Same Same. Sketchup introduced me to Ruby.
-
@dan rathbun said:
You don't have a choice. The only way to have Sketchup perform an event-driven task, (because Sketchup "owns" the event loop,) is to use one of the Ruby API's observer classes that reacts to changes in a list-like object.
I stand corrected. I was thinking event-driven.
mtalbott, showed a polling means using the UI.start_timer block. Forget about that... should be much eaiser to implement.
-
@rabidcicada said:
Does Sketchup have it's own local copy of Ruby 1.8.6 etc?
Yes, BUT only the interpreter, which loads ONLY the Ruby Core modules and classes. (Sketchup then loads it's own API extension modules and classes, and additions to a few of the Core classes: Array, Length, Numeric and String.)
On MS Windows, the interpreter binary that Sketchup loads, is in the Sketchup program folder, and is named msvcrt-ruby18.dll; and is an exact copy of the dll (of the same name,) that resides in the bin folder of a full install.
Win32 Sketchup vers 6 & 7, have the v1.8.0 (initial release) interpreter DLL.
Win32 Sketchup ver 8 has the v1.8.6-p287 interpreter DLL.On Mac, for vers 6 thru 8 (current MR,) the interpreter Sketchup loads (by default,) is a framworkized Ruby v1.8.5 (initial release,) Core only, that resides in subdirectories, beneath the Sketchup application directory.
@rabidcicada said:
Or are you saying that I must use that version if I am to successfuly drive sketchup externally?
It certainly helps to use the same version that Sketchup loads.
On Windows, it is very easy to force Sketchup to load a specific Ruby version (in the 1.8.x branch,) by replacing the interpreter DLL in the Sketchup program folder.)
See my post on this subject: Ruby Interpreter DLLs (Win32).On Mac, it requires the changing of symbolic links.
This Stackoverflow post shows how, but realize you will be overwriting the default links. (Mac users should backup the originals first.)@rabidcicada said:
Should I separately install Ruby 1.8.6?
Ruby v1.8.6-p287 Windows One-Click Installer (self-extracting zip installer.)
Since MS Windows does not come "out-of-the-box" with a full Ruby install (like a Mac does,) it will be to your benefit to do a full Ruby install, so you can have access to the extended Ruby library files.
See my !load_paths.rb script, to add paths for the standard lib folders, to the $LOAD_PATH array in Sketchup embedded Ruby.@rabidcicada said:
Thanks for the pointer to Alexs stuff...I'll play with that also.
Alex's SCF Plugin topic: [plugin] Ruby Code Editor
which links to his website: SketchUp Ruby Code Editor@rabidcicada said:
I am SEVERELY unfamiliar with hacking up sketchup...FYI.
When you install the full Ruby, you will have the CHM Ruby Language reference for both the Core and the Extended Libraries, in the doc folder. (Make shortcuts to them.. or copy the CHM files someplace, your desktop, wherever you can find them easily, as you will be refering to them constantly.) Or... (in the interim,) you can download the CHM from my post in the SCF Ruby References topic here.
Also, the full install comes with a PDF of Huw Collingbourne's THE BOOK OF RUBY which may help.You can click the Ruby Resources link in my signature... this will lead you to a multitude of info, (tutorials, downloadable books etc.)
See the [Code Snippets] sticky post on C/C++ Extensions
.. which has a link to the C source for Ruby. You may wish to read the source for Ruby's standard Socket library for ideas.
EDIT: (2011-05-08) Sorry.. had the one-click installer link pointing to the C source. Corrected links.
-
Sorry... had the link in my previous post pointing to the C sourcecode archive. (Corrected links in original post.)
Ruby v1.8.6-p287 Windows One-Click Installer (self-extracting zip installer.)
Advertisement