-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use socket for SC -> JS communication #37
Conversation
You will want to use the test suite. Once you turn on jest it runs those tests before you can even blink. I'm not sure what the address in use error is in here: https://travis-ci.org/crucialfelix/supercolliderjs/jobs/280304528 |
Looks great so far ! |
OK, it's passing all the tests. I've done a little refactoring (I moved the socket stuff into Also closes #35. I've tested it a bit running from node directly and also playing around in Atom, and AFAICT it seems to be working fine. Definitely kick the tires and let me know if there are issues. |
I will definitely kick tires and do code review. Great work ! |
I haven't yet had a chance to test this. I will probably want to merge it to develop, but then there are some issues that need to be solved before a release. Currently you could only run one supercollider.js at the same time. I often run many. So the socket should be able to use unix domain sockets like But we should also support open TCP connections. It would be fun (and possibly not entirely useless) to connect to an sclang running on a beagleboard. |
src/lang/internals/sclang-io.js
Outdated
READY: 'ready' | ||
CONNECTING: 'connecting', | ||
READY: 'ready', | ||
CAPTURING: 'capturing', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you want to keep capturing state in a separate variable. These states are broadcast to listeners mainly for watching when sclang is up and ready (or died etc.) You wouldn't want to broadcast a change any time there is an incoming response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the years go by quick. I do apologize. I have looked at it a few times, but kids, jobs and other ambitions are pretty distracting.
The main goal of the PR (as I understand it) is to allow larger responses to be returned from sclang without breaking or getting co-mingled with other STDOUT that sclang may be burping up.
The main blocker to landing this is that it doesn't just add a TCP connection for returning responses. It also reworks the state machine and parsing. It adds more places for things to go wrong or for internal state to get out of sync.
It's a bit messier than the previous implementation and doesn't fix or change anything.
The main win here is the TCP socket for responses.
So I'd like to include just that, make it optional, configurable port and leave the rest of the state and IO parser as is. It would still be able to parse STDOUT results. The TCP version if enabled would allow results to be sent that way. Probably it could send the Errors back too. They can get quite big.
I know you've moved on to other exciting things in life, so I'm not asking for changes.
@@ -24,6 +25,9 @@ import { SclangResultType } from '../Types'; | |||
// 'any' just opts out of type checking | |||
type ChildProcessType = any; // child_process$ChildProcess; | |||
|
|||
const RESULT_LISTEN_HOST = 'localhost'; | |||
const RESULT_LISTEN_PORT = 22967; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TCP port is hardcoded, and so there could only be one sclang
running at a time.
stateWatcher.on(name, (...args) => { | ||
this.emit(name, ...args); | ||
}); | ||
} | ||
stateWatcher.on('state', newState => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that the stateWatcher event listener should be keeping track of changes. It is only supposed to re-emit the event. The consumer can keep track and ignore if it needs to.
The reason you had to put this in is because you added "CAPTURING" as a new state, but the intention of the states is only to track the major lifecycle of the sclang
interpreter.
As you have it now it would go back and forth from READY to CAPTURING.
I think you are assuming that there can only be one interpreter call active at a time. In fact, you can launch many of them and each one many do async things over in supercollider lang, and return results or errors at any point in the future.
compiled = code.compile; | ||
|
||
if(compiled.isNil, { | ||
"\nSUPERCOLLIDERJS:%:CAPTURE:END".format(guid).postln; | ||
"SUPERCOLLIDERJS:%:CAPTURE:END".format(guid).postln; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The newlines are actually necessary in case sclang posted, not postln. The SUPERCOLLIDERJS messages need to always start on a newline.
if(resultSocket.isNil, { | ||
"resultSocket must be connected before returning data".error; | ||
}, { | ||
var msg = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could have just done this.stringify(msg)
. Looks like you tried to do that but ()
is an Event in sc, not a simple dict.
@@ -15,9 +15,12 @@ export const STATES = { | |||
COMPILED: 'compiled', | |||
COMPILING: 'compiling', | |||
COMPILE_ERROR: 'compileError', | |||
READY: 'ready' | |||
READY: 'ready', | |||
CAPTURING: 'capturing' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It adds a new STATES "CAPTURING", but the intention of the states is only to track the major lifecycle of the sclang interpreter. As you have it now it would go back and forth from READY to CAPTURING.
I think you are assuming that there can only be one interpreter call active at a time. In fact, you can launch many of them and each one many do async things over in supercollider lang, and return results or errors at any point in the future.
@@ -33,70 +36,83 @@ export const STATES = { | |||
*/ | |||
export class SclangIO extends EventEmitter { | |||
states: Object; | |||
responseCollectors: Object; | |||
capturing: Object; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
responseCollectors
and capturing
is a dict storing responses. There may be more than one active at any time. They can post overlapping messages to STDOUT.
if (!stf.re.global) { | ||
break; | ||
} | ||
var inputLines = input.split('\n'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see that parsing line by line is possibly better than multiline matching. I can also see a cleaner way to do it (than either my previous implementation or the one in this PR). But by changing this as much as you did, I'm now confused and can't quite tell what might break.
Yes, the tests run, but the reason I hadn't merged before is because I can't be sure now.
Thanks for taking the time to look at this, and sorry the PR co-mingled too many different things. A bit after working on this PR I switched back to using the main SC IDE, so unfortunately I don't think I'll have the time to work on this any time in the near future. I won't be offended if you want to close the PR, or rip out any bits that might be useful if you still want to switch to TCP transport. |
I'm definitely going to transplant the TCP transport and make that work. I will try to cherry pick it in if I can, but definitely give you the credit. Thanks! |
This PR still needs some testing but seems functional. The two main parts are:
interpret
call from SCLang. This way you can rely on getting the databack intact even if there's other stuff going over STDOUT. For example, if
you're printing OSC debugging info while at the same time requesting data
to use for auto-complete in the editor.
planning on doing this as part of this PR but I ran into trouble where
sometimes the state machine wasn't grabbing the CAPTURE:END blocks. IMO
this setup is easier to read and no longer makes any assumptions about
alignment of the callback data.
I still need to walk through the state machine to verify it's correctly
handling all the cases that the old one did before this is ready for full
review, but I figured I'd post progress in case you wanted to take a glance.