The following forum(s) have migrated to Microsoft Q&A (Preview): Developing Universal Windows apps!
Visit Microsoft Q&A (Preview) to post new questions.

Learn More

 locked
How to send commands via network to a TCP/IP-SerialPort server? RRS feed

  • Question

  • Hello,

    I would like to develop an app which remotely operates the Meade Instruments telescopes. Meade's Autostar Suite is delivered with a program called "NetScope" which is actually a telescope Server, listening on TCP/IP and sends commands via Serial port to the telescope.

    Commands Looks like this:

    A - Alignment Commands
    :Aa# Start Telescope Automatic Alignment Sequence [Autostar II/RCX400 only]
    Returns:
    1: When complete (can take several minutes).
    0: If scope not AzEl Mounted or align fails
    :AL# Sets telescope to Land alignment mode
    Returns: nothing
    :AP# Sets telescope to Polar alignment mode
    Returns: nothing
    :AA# Sets telescope the AltAz alignment mode
    Returns: nothing

    Full command set is over here: Meade Telescope Command set

    The telescope Server is located at 192.168.1.110, port 5401

    The question is, how do I talk using C#, with the telecope Server?

    There are three different kind of commands,

    1. :Aa# - Returns a string
    2. :AL# - Returns nothing
    3. :B<n># - Where "n" is a passed ASCII number (command with user Input value)

    Many thanks in advance for any help...

    Wednesday, May 21, 2014 7:19 PM

All replies

  • Hi Tech-49,

    you could use a StreamSocket for that. Available when developing for Windows 8.1:

    Windows.Networking.Sockets.StreamSocket.

    Looks like this is a special kind of protocol used for that telescope. I don't know how to use it, but as it's text-based, it's similar to protocols like FTP, that build on top of TCP/IP. Maybe you remember this ISO/OSI-layer-model-stuff. ;-)

    To give you a clue, here a small test-method I've done to access my ftp on port 21 and sending FTP-messages. You can do the same with your specific protocol:

    using (var socket = new StreamSocket())
                            {
                                var hostName = new HostName("192.168.xxx.xxx");
                                await socket.ConnectAsync(hostName, "21");
                                using (var writer = new DataWriter(socket.OutputStream))
                                using (var reader = new DataReader(socket.InputStream))
                                {
                                    reader.InputStreamOptions = InputStreamOptions.Partial;
                                    var message = await ReadMessageAsync(reader);
    
                                    await WriteMessageAsync(writer, "USER xxxxx");
                                    message = await ReadMessageAsync(reader);
    
                                    await WriteMessageAsync(writer, "PASS xxxxxxx");
                                    message = await ReadMessageAsync(reader);
    
                                    await WriteMessageAsync(writer, "CWD /testit");
                                    message = await ReadMessageAsync(reader);
    
    
                                    // Start Passive mode
                                    await WriteMessageAsync(writer, "PASV");
                                    message = await ReadMessageAsync(reader);
    
                                    var passiveModeConnection = new PassiveModeConnection(message);
                                    await passiveModeConnection.WriteContentAsync(file.Value);
    
                                    await WriteMessageAsync(writer, string.Format("STOR {0}", file.Key));
                                    message = await ReadMessageAsync(reader);
                                    message = await ReadMessageAsync(reader);
                                }
                            }

    And here the methos WriteMessageAsync, ReadMessageAsync (Note that the "\r\n" attached to the message is FTP-based. FTP-commands always need to end with a \r\n.

            private async Task WriteMessageAsync(DataWriter writer, string message)
            {
                if (!message.EndsWith("\r\n"))
                    message += "\r\n";
    
                writer.WriteString(message);
                await writer.StoreAsync();
            }

     const uint chunksize = 4096;
            private async Task<string> ReadMessageAsync(DataReader reader)
            {
                var stringBuilder = new StringBuilder();
    
                var bytesAvailable = await reader.LoadAsync(chunksize);
    
                while (bytesAvailable > 0)
                {
                    var byArray = new byte[bytesAvailable];
                    reader.ReadBytes(byArray);
                    stringBuilder.Append(Encoding.UTF8.GetString(byArray, 0,
                        Convert.ToInt32(bytesAvailable)));
    
                    if (bytesAvailable < chunksize)
                    {
                        break;
                    }
                    bytesAvailable = await reader.LoadAsync(chunksize);
                }
                return stringBuilder.ToString();
            }

    And as FTP requires a second connection to upload the data, I created that class that is used in the very first snippet:

     public class PassiveModeConnection
        {
            public PassiveModeConnection(string passiveModeMessage)
            {
                // Message is something like this: "227 Entering Passive Mode (77,236,96,206,145,75).\r\n"
                // => (77,236,96,206,145,75) is a1,a2,a3,a4,p1,p2
                //    a1.a2.a3.a4 is the IP address and p1*256+p2 is the port
                var numberString = passiveModeMessage.Substring(passiveModeMessage.IndexOf('(') + 1,
                    passiveModeMessage.IndexOf(')') - (passiveModeMessage.IndexOf('(') + 1));
                var array = numberString.Split(',');
                IpAddress = string.Format("{0}.{1}.{2}.{3}", array[0], array[1], array[2], array[3]);
    
    
                Port = (int.Parse(array[4]) * 256 + int.Parse(array[5])).ToString();
            }
            public string IpAddress { get; set; }
            public string Port { get; set; }
    
    
            public async Task WriteContentAsync(byte[] content)
            {
                using (var passiveModeSocket = new StreamSocket())
                {
                    var hostName = new HostName(IpAddress);
                    await passiveModeSocket.ConnectAsync(hostName, Port);
                    using (var writer = new DataWriter(passiveModeSocket.OutputStream))
                    {
                        writer.WriteBytes(content);
                        await writer.StoreAsync();
                        writer.DetachStream();
                    }
                }
            }
        }


    Thomas Claudius Huber

    "If you can't make your app run faster, make it at least look & feel extremly fast"

    My latest app: The "Womanizer" :-)
    twitter: @thomasclaudiush
    homepage: www.thomasclaudiushuber.com
    author of: ultimate Windows Store Apps handbook | ultimate WPF handbook | ultimate Silverlight handbook

    Wednesday, May 21, 2014 7:36 PM
  • Hi Tec-49,

    I did some reading up on the Meade Autostar suite, well I skimmed through the documentation on their site so bare with me here.

    From what I understand is that you start NetScope as a server (on the machine connected via Serial to the telescope) with a configured port of your choosing, using another computer you connect remotely to the server you just started (via the Autostar suite).

    From what I read it won't be as simple as sending the Telescope Command (via TCP) to the remote server using plain text socket connection, for example the connection could involve password authentication (if setup).
    I'm almost positive they'll be using a custom set of packets for the communication, that said I could be wrong and they might be using something well known?

    Now I don't want to deter you from making your application, on the contrary I think it'll be fun to figure out how it works, I'd be happy to help you figure it.

    Regards,
    William.

    Thursday, May 22, 2014 8:34 AM
  • Hi Tec-49,

    It appears that I was in fact incorrect, I downloaded Meade's AutoStar suite and did some packet captures,  seems that the commands are sent in plain-text.

    Here's a quick look at the packets (captured with Wireshark) send back and forth.

    Server: Requesting password:

    Client: Sending password:

    I'd upload more images but there's a limit per response, if you want to see more I'll happily post them.

    Anyway, this makes things a lot easier, I have taken some of the code Thomas submitted above and reworked it to your needs.

          using (var socket = new StreamSocket())
          {
            var hostName = new HostName("192.168.1.110");
            await socket.ConnectAsync(hostName, "5401");
            using (var writer = new DataWriter(socket.OutputStream))
            using (var reader = new DataReader(socket.InputStream))
            {
              reader.InputStreamOptions = InputStreamOptions.Partial;
              var message = await ReadMessageAsync(reader);
    
              #region Password check
    
              //Server is requesting a password from the client.
              if (message.Equals("PSW?", StringComparison.InvariantCultureIgnoreCase))
              {
                //Lets send the password.
                await WriteMessageAsync(writer, "password");
                message = await ReadMessageAsync(reader);
    
                //If the server returns an empty string then the password failed (multiple times).
                if (string.IsNullOrEmpty(message))
                {
                  throw new NullReferenceException();
                }
    
                //If the server returns 'PSW?' again (and again), the password is incorrect.
                if (message.Equals("PSW?", StringComparison.InvariantCultureIgnoreCase))
                {
                  throw new Exception("Password is incorrect.");
                }
              }
    
              #endregion
    
              #region Connection result
    
              //If our message wasn't 'OK' then we abort, connection rejected by server?
              if (!message.Equals("OK", StringComparison.InvariantCultureIgnoreCase))
              {
                throw new Exception();
              }
    
              #endregion
    
              #region Handshake mechanism?
    
              await WriteMessageAsync(writer, ":LK0#");
              await WriteMessageAsync(writer, ":LK1#");
              message = await ReadMessageAsync(reader);
    
              #endregion
    
              #region Unexpected message after handshake?
    
              //After we have connected and the handshake is complete the server will return 'Yes#', if not then abort.
              if (!message.Equals("Yes#", StringComparison.InvariantCultureIgnoreCase))
              {
                throw new Exception("Unexpected response from server!");
              }
    
              #endregion
    
              #region Telescope commands
    
              #region :Aa# - Start Telescope Automatic Alignment Sequence
              await WriteMessageAsync(writer, ":Aa#");
              message = await ReadMessageAsync(reader);
    
              int aaResult;
              if (Int32.TryParse(message, out aaResult))
              {
                if (aaResult.Equals(1))
                {
                  //Automatic alignment sequence completed
                }
                else
                {
                  //Scope not AzEl mounted or align failed
                }
              }
              #endregion
    
              #region :AL# - Sets telescope to Land alignment mode
              await WriteMessageAsync(writer, ":AL#");
              #endregion
    
              #region :B<n># - Set Reticle flash rate to <n> (an ASCII expressed number)
              await WriteMessageAsync(writer, string.Format(":B{0}#", 0));
              #endregion
    
              #endregion
            }
          }

    Hopefully that'll get you on the right track.

    Regards,
    William.


    • Edited by wdcossey Thursday, May 22, 2014 12:30 PM
    Thursday, May 22, 2014 12:28 PM
  • Thank you William, I'll test it as soon the rain outside is gone. (scope is under cover).
    Thursday, May 22, 2014 3:55 PM
  •  I was experimenting a litte bit with "putty". At first connection the window showed me "PSW?". I send the correct Password (set it to 1234) and it responded ok. But None of the meade commands reached the telescope. The reason is perhaps that the NetScope is designed to operate a scope and a dome. The next Problem to solve is, to tell netscope to which device (com port) send the commands. I guess there must be some Kind of prefix...
    Friday, May 23, 2014 3:15 PM
  • Hi Tech-49, Unfortunately I do not own a Meade telescope (or any telescope for that matter), I only made an assumption about the commands to send to the server from my limited testing.

    Possibly the best solution would be for you to use Wireshark to capture the packets, see what commands are sent back and forth (using the standard Meade software).

    If there's a public telescope I could contract too, I'd happily help you?

    That said, I'll try do some digging and see what else I can find out.

    Regards,
    William.

    • Edited by wdcossey Friday, May 23, 2014 9:31 PM
    Friday, May 23, 2014 9:26 PM