3

I am using the Photoshop Connection SDK to get my iPad app connected to Photoshop.

(The SDK with all sample iOS projects can be downloaded here if you want to look closer: http://www.adobe.com/devnet/photoshop/sdk.html)

While I have it working, its hard for me to solve my issue as I don't entirely understand the networking between the iPad and Photoshop. So here is my issue:

NSString *s1 = [NSString stringWithUTF8String:"app.activeDocument.layers[0].name;"];
NSData *dataToSend = [s1 dataUsingEncoding:NSUTF8StringEncoding];
[self sendJavaScriptMessage:dataToSend];
artLayerName_transaction = transaction_id -1;

There is a little snippet of code, to send a message asking for the name of layer at index 0. That works great. However, let's say I try and send the same message straight after that but for index 1 as well.

Both messages are sent, but only one returns its string. The string is returned here:

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)streamEvent;

If you see the example projects its just the same, each message with its own transaction id as well. The method goes through a bunch of decrypting and things to receive that string and then reaches this:

NSString *string = [[NSString alloc] initWithBytes:received_data length:received_length encoding:NSUTF8StringEncoding];
if (content != 1)
{
     if (transaction == artLayerName_transaction)
         {
              [self processLayerName:string];
              needOutput = NO;
         }
}

I've out the full method at the bottom for full analysing.

It checks that its receiving the specific message so I can then take the result (string, my layer name) and do what I like with it. However when I try and send more than one message with the same transaction id I only get one of the two results. In the code above, string gives me both my layer names, but the if statement is only called once.

Is there a known way around sending several messages at once? I have tried getting an array back rather than several strings without luck as well.

Apparently I need to modify the code to accept more then message. However I don't really understand the code well enough, so please also explain any of the principals behind it.

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)streamEvent
{
    NSInputStream * istream;
    switch(streamEvent)
    {
        case NSStreamEventHasBytesAvailable:;

            UInt8 buffer[1024];
            unsigned int actuallyRead = 0;

            istream = (NSInputStream *)aStream;
            if (!dataBuffer)
            {
                dataBuffer = [[NSMutableData alloc] initWithCapacity:2048];
            }

            actuallyRead = [istream read:buffer maxLength:1024];

            [dataBuffer appendBytes:buffer length:actuallyRead];

            // see if we have enough to process, loop over messages in buffer
            while( YES )
            {

                // Did we read the header yet?
                if ( packetBodySize == -1 )
                {
                    // Do we have enough bytes in the buffer to read the header?
                    if ( [dataBuffer length] >= sizeof(int) ) {
                        // extract length
                        memcpy(&packetBodySize, [dataBuffer bytes], sizeof(int));
                        packetBodySize = ntohl( packetBodySize );       // size is in network byte order

                        // remove that chunk from buffer
                        NSRange rangeToDelete = {0, sizeof(int)};
                        [dataBuffer replaceBytesInRange:rangeToDelete withBytes:NULL length:0];
                    }
                    else {
                        // We don't have enough yet. Will wait for more data.
                        break;
                    }
                }

                // We should now have the header. Time to extract the body.
                if ( [dataBuffer length] >= ((NSUInteger) packetBodySize) )
                {
                    // We now have enough data to extract a meaningful packet.
                    const int kPrologLength = 16;
                    char *buffer = (char *)[dataBuffer bytes];

                    // if incoming message is color change, then don't display message
                    BOOL needOutput = YES;

                    // fetch the communication status
                    unsigned long com_status = *((unsigned long *)(buffer + 0));
                    com_status = ntohl( com_status );

                    // decrypt the message
                    size_t decryptedLength = (size_t) packetBodySize - 4;  // don't include com status

                    int skip_message = 0;

                    if (com_status == 0 && sCryptorRef)
                    {
                        PSCryptorStatus decryptResult = EncryptDecrypt (sCryptorRef, false, buffer+4, decryptedLength, buffer+4, decryptedLength, &decryptedLength);

                        if (kCryptorSuccess != decryptResult)
                        {
                            // failed to decrypt.  Ingore messageg and disconnect
                            skip_message = 1;
                            [self logMessage:@"ERROR:  Decryption failed.  Wrong password.\n" clearLine:NO];
                        }
                    }
                    else
                    {
                        if (com_status != 0)
                        [self logMessage:@"ERROR: Problem with communication, possible wrong password.\n" clearLine:NO];

                        if (!sCryptorRef)
                        [self logMessage:@"ERROR: Cryptor Ref is NULL, possible reason being that password was not supplied or password binding function failed.\n" clearLine:NO];
                    }

                    // Interpret encrypted section
                    if (!skip_message)
                    {
                        // version, 32 bit unsigned int, network byte order
                        unsigned long protocol_version = *((unsigned long *)(buffer + 4));
                        protocol_version = ntohl( protocol_version );

                        if (protocol_version != 1)
                        {
                            // either the message is corrupted or the protocol is newer.
                            [self logMessage:@"Incoming protocol version is different the expected. (or the message is corrupted.)  Not processing.\n" clearLine:NO];
                            skip_message = 1;
                        }

                        if (!skip_message)
                        {
                            // transaction, 32 bit unsigned int, network byte order
                            unsigned long transaction = *((unsigned long *)(buffer + 8));
                            transaction = ntohl( transaction );

                            // content type, 32 bit unsigned int, network byte order
                            unsigned long content = *((unsigned long *)(buffer + 12));
                            content = ntohl( content );

                            unsigned char *received_data = (unsigned char *)(buffer+kPrologLength);
                            int received_length = (decryptedLength-(kPrologLength-4));

                            if (content == 3) // image data
                            {
                                // process image data
                                unsigned char image_type = *((unsigned char *)received_data);
                                [self logMessage:@"Incoming data is IMAGE.  Skipping\n" clearLine:NO];

                                if (image_type == 1) // JPEG
                                {
                                    [self logMessage:@"By the way, incoming image is JPEG\n" clearLine:NO];
                                }
                                else if (image_type == 2) // Pixmap
                                {
                                    [self logMessage:@"By the way, incoming image is Pixmap\n" clearLine:NO];
                                }
                                else
                                {
                                    [self logMessage:@"Unknown image type\n" clearLine:NO];
                                }
                            }
                            else
                            {
                                // Set the response string
                                NSString *string = [[NSString alloc] initWithBytes:received_data length:received_length encoding:NSUTF8StringEncoding];

                                //NSLog(@"string: %@\n id:%li", string, transaction);

                                // see if this is a response we're looking for
                                if (content != 1)
                                {                                    
                                    if (transaction == foregroundColor_subscription || transaction == foregroundColor_transaction)
                                    {
                                        [self processForegroundChange:string];
                                        needOutput = NO;
                                    }
                                    if (transaction == backgroundColor_subscription || transaction == backgroundColor_transaction)
                                    {
                                        [self processBackgroundChange:string];
                                        needOutput = NO;
                                    }
                                    if (transaction == tool_transaction)
                                    {
                                        [self processToolChange:string];
                                        needOutput = NO;
                                    }
                                    if (transaction == artLayerName_transaction)
                                    {
                                        [self processLayerName:string];
                                        needOutput = NO;
                                    }
                                }
                                //Tells me about every event thats happened (spammy tech nonsence, no good for user log)
                                //if (needOutput) [self logMessage:string clearLine:NO];
                            }
                        }

                    }

                    // Remove that chunk from buffer
                    NSRange rangeToDelete = {0, packetBodySize};
                    [dataBuffer replaceBytesInRange:rangeToDelete withBytes:NULL length:0];

                    // We have processed the packet. Resetting the state.
                    packetBodySize = -1;
                }
                else
                {
                    // Not enough data yet. Will wait.
                    break;
                }
            }

            break;
        case NSStreamEventEndEncountered:;
            [self closeStreams];
            [self logMessage:[NSString stringWithFormat: @"%@ End encountered, closing stream.\n", outputMessage.text] clearLine:NO];
            break;
        case NSStreamEventHasSpaceAvailable:
        case NSStreamEventErrorOccurred:
        case NSStreamEventOpenCompleted:
        case NSStreamEventNone:
        default:
            break;
    }
}

EDIT:

While this has worked, it has bumped me into another issue. I looked at the psconnection sample and saw what you were talking about, however I haven't been using the full framework but have it connect like in the other sample projects. I very simply increase my transaction id by 1 every time it comes to process a layer name such as in the code above.

Like this:

if (transaction == docName_transaction)
{
     docName_transaction++;
     [self processDocName:string];
}

This works for the most part, however if I do this to another transaction id at the same time I get overlap. Meaning I end up processing the result for one id at the wrong time. Say I am getting both the total number of docs and the name of each one.

So I have two if statements like above, but I end up processing the total docs in both if statements and I can't see how to atop this overlap. Its fairly important to be able to receive multiple messages at once.

4

1 回答 1

0

I just downloaded samples, and as far as i can see, you should use unique transaction IDs for transactions that are waiting for response.

I mean if you send layer_name request with transactionID 1, you cannot use 1, until it's response is received.

So better case will be, store your transactionID's in a Dictionary (transactionID as key), and message type as value.

just like:

transactions[transaction_id] = @"layername";

and when you receive response:

use: transactions[transaction_id] to get the message type (ex: layername) behave according to this.

You can also put some other details to transactions dictionary (you can put a dictionary containing all information, which command, on which object, etc)

于 2013-03-09T14:32:23.853 回答