In [1]:
#Socket client example in python
 
import socket   #for sockets
import sys  #for exit
import struct
from __future__ import division
import time
import sys,io
import string
import asynchat

In [2]:
def dprint(m):
    for c in m:
        if ord(c)==0x3b :
            print
        print "0x%02x" %ord(c),
    print
    
def checksum(msg):
    return ((~sum([ord(c) for c in msg]) + 1) ) & 0xFF

In [3]:
class scope(asynchat.async_chat):
    
    def __init__(self, addr='1.2.3.4', port=2000):
        asynchat.async_chat.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((addr, port))
        self.ibuffer = []
        self.obuffer = ''
        self.reading_headers = True
        self.handling = False
        self.set_terminator('*HELLO*')
        self.startup=True
        
    # grab some more data from the socket,
    # throw it to the collector method,
    # check for the terminator,
    # if found, transition to the next state.

    def handle_read (self):

        try:
            data = self.recv (self.ac_in_buffer_size)
        except socket.error, why:
            if why.args[0] in _BLOCKING_IO_ERRORS:
                return
            self.handle_error()
            return

        self.ac_in_buffer = self.ac_in_buffer + data

        # Continue to search for self.terminator in self.ac_in_buffer,
        # while calling self.collect_incoming_data.  The while loop
        # is necessary because we might read several data+terminator
        # combos with a single recv(4096).

        while self.ac_in_buffer:
            lb = len(self.ac_in_buffer)
            terminator = self.get_terminator()
            if not terminator:
                # no terminator, collect it all
                self.collect_incoming_data (self.ac_in_buffer)
                self.ac_in_buffer = ''
            elif isinstance(terminator, int) or isinstance(terminator, long):
                # numeric terminator
                n = terminator
                if lb < n:
                    self.collect_incoming_data (self.ac_in_buffer)
                    self.ac_in_buffer = ''
                    self.terminator = self.terminator - lb
                else:
                    self.collect_incoming_data (self.ac_in_buffer[:n])
                    self.ac_in_buffer = self.ac_in_buffer[n:]
                    self.terminator = 0
                    self.found_terminator()
            else:
                # 3 cases:
                # 1) end of buffer matches terminator exactly:
                #    collect data, transition
                # 2) end of buffer matches some prefix:
                #    collect data to the prefix
                # 3) end of buffer does not match any prefix:
                #    collect data
                terminator_len = len(terminator)
                index = self.ac_in_buffer.find(terminator)
                if index != -1:
                    # we found the terminator
                    if index > 0:
                        # don't bother reporting the empty string (source of subtle bugs)
                        # Sometimes one needs empty strings!
                        pass
                    #print 'Term found'
                    sys.stdout.flush()
                    self.collect_incoming_data (self.ac_in_buffer[:index])
                    self.ac_in_buffer = self.ac_in_buffer[index+terminator_len:]
                    # This does the Right Thing if the terminator is changed here.
                    self.found_terminator()
                else:
                    # check for a prefix of the terminator
                    index = asynchat.find_prefix_at_end (self.ac_in_buffer, terminator)
                    if index:
                        if index != lb:
                            # we found a prefix, collect up to the prefix
                            self.collect_incoming_data (self.ac_in_buffer[:-index])
                            self.ac_in_buffer = self.ac_in_buffer[-index:]
                        break
                    else:
                        # no prefix, collect it all
                        self.collect_incoming_data (self.ac_in_buffer)
                        self.ac_in_buffer = ''

        
    def collect_incoming_data(self, data):
        '''Buffer the data'''
        #print 'Collect:',
        #dprint(data)
        sys.stdout.flush()
        self.ibuffer.append(data)
    
    def found_terminator(self):
        if self.startup :
            self.set_terminator('\x3b')
            self.startup=False
            self.ibuffer=[]
            print 'Hello!'
            sys.stdout.flush()
            return
        if self.reading_headers :
            print 'msg:',
            self.reading_headers=False
            self.ibuffer=[]
            self.set_terminator(1)
        elif self.handling :
            self.handling=False
            self.reading_headers=True
            self.set_terminator('\x3b')
            #print self.ibuffer
            self.msg=''.join(self.ibuffer)
            dprint(self.msg)
            if checksum(self.msg[:-1])!=ord(self.msg[-1]) :
                print 'Checksum error!'
        else :
            self.handling=True
            self.msglen=ord(self.ibuffer[0][0])
            print '(%2d)' % self.msglen,
            self.set_terminator(self.msglen+1)
        sys.stdout.flush()

In [7]:
nse=scope('10.24.48.10')
asynchat.asyncore.loop(count=25)
nse.socket.close()


Hello!
msg: ( 4) 0x04 0x0d 0x11 0x24 0x05 0xb5
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x00 0xba
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x05 0xb5
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x00 0xba
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x05 0xb5
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x00 0xba
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x05 0xb5
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x00 0xba
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x05 0xb5
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x00 0xba
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x25 0x05 0xb4
msg: ( 3) 0x03 0x11 0x0d 0x25 0xba
msg: ( 4) 0x04 0x0d 0x11 0x24 0x00 0xba
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x05 0xb5
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x00 0xba
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x25 0x05 0xb4
msg: ( 3) 0x03 0x11 0x0d 0x25 0xba
msg: ( 4) 0x04 0x0d 0x11 0x24 0x00 0xba
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x05 0xb5
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x00 0xba
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x25 0x05 0xb4
msg: ( 3) 0x03 0x11 0x0d 0x25 0xba
msg: ( 4) 0x04 0x0d 0x11 0x24 0x00 0xba
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x05 0xb5
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x00 0xba
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb
msg: ( 4) 0x04 0x0d 0x11 0x24 0x05 0xb5
msg: ( 3) 0x03 0x11 0x0d 0x24 0xbb

In [8]:
nse.socket.close()

In [119]:
?? asynchat.async_chat

In [10]:
targets={'ANY':'\x00',
         'MB' :'\x01',
         'HC' :'\x04',
         'HC+':'\x0d',
         'AZM':'\x10',
         'ALT':'\x11',
         'APP':'\x20',
         'GPS':'\xb0',
         'WiFi':'\xb5',
         'BAT':'\xb6',
         'CHG':'\xb7',
         'LIGHT':'\xbf'
        }
commands={
          'MC_GET_POSITION':'\x01',
          'MC_GOTO_FAST':'\x02',
          'MC_SET_POSITION':'\x04',
          'MC_SET_POS_GUIDERATE':'\x06',
          'MC_SET_NEG_GUIDERATE':'\x07',
          'MC_LEVEL_START':'\x0b',
          'MC_GOTO_SLOW':'\x17',
          'MC_SEEK_INDEX':'\x19',
          'MC_SET_AUTOGUIDE_RATE':'\x46',
          'MC_GET_AUTOGUIDE_RATE':'\x47',
          'GET_VER':'\xfe',
          'GET_BATT_STATUS':'\x10',
         }

trgid={}
for k in targets.keys():
    trgid[ord(targets[k])]=k
cmdid={}
for k in commands.keys():
    cmdid[ord(commands[k])]=k
    
def checksum(msg):
    return ((~sum([ord(c) for c in msg]) + 1) ) & 0xFF

def f2dms(f):
    d=360*f
    dd=int(d)
    mm=int((d-dd)*60)
    ss=(d-dd-mm/60)*3600
    return dd,mm,ss

def parse_pos(dat,s=None,d=None):
    if len(dat)==3 :
        pos=struct.unpack('!i','\x00'+dat)[0]/2**24
        return u'%03d°%02d\'%04.1f"' % f2dms(pos)
    else :
        return ''

def parse_bat(dat):
    #dprint(dat)
    batstat={
        '\x00':'LOW',
        '\x01':'MEDIUM',
        '\x02':'HIGH',
    }
    if len(dat)==6 :
        return u'%6s: %.4fV %s ' % ( batstat[dat[1]],
                                     struct.unpack('!i',dat[2:])[0]/1e6,
                                     'Charging' if dat[0]=='\x01' else 'Discharging' )
    else :
        return ''
    
def parse_10(dat,s=None,d=None):
    if s==0xb6 or d==0xb6 :
        return parse_bat(dat)
    else :
        return ''.join(['0x%02x ' % ord(b) for b in dat])
    
parsers={
    '\x01':parse_pos,
    '\x02':parse_pos,
    '\x04':parse_pos,
    '\x06':parse_pos,
    '\x07':parse_pos,
    '\x17':parse_pos,
    '\x10':parse_10,
    '\x11':parse_10,
}

def send_cmd(sck, src='APP', trg='AZM', cmd='GET_VER', data=''):
    pre='\x3b'
    msg=targets[src]+targets[trg]+commands[cmd]+data
    msg=chr(len(msg))+msg
    sck.sendall(pre+msg+chr(checksum(msg)))


def split_msgs(r,debug=False):
    l=r.find('\x3b')+1
    p=l
    ml=[]
    while p>-1:
        p=r.find('\x3b',l)
        #if debug : print l, p
        ml.append(r[l:p]+r[p])
        l=p+1
    if debug : print ml
    return ml
    
def parse_msg(m, debug=False):
    '''
    Parse bytes byond 0x3b. 
    Do not pass the message with preamble!
    '''
    l=ord(m[0])+1
    msg=m[:l]
    if debug :
        print 'Parse:',
        dprint(msg)
    if  chr(checksum(msg)) != m[l] :
        print 'Checksum error: %x vs. %02x' % (checksum(msg) , ord(m[l]))
        dprint(m)
    l,src,dst,mid=struct.unpack('4B',msg[:4])
    dat=msg[4:l+1]
    #print 'len:', l
    return l, src, dst, mid, dat


def print_msg(l,s,d,i,dat):
    try :
        print '[%d] %4s => %4s (%12s): ' % (l,trgid[s],trgid[d],cmdid[i]),
    except KeyError :
        print '[%d] %02x => %02x (%02x): ' % (l,s,d,i),
    if chr(i) in parsers:
        print parsers[chr(i)](dat,s,d),
    else :
        dprint(dat)

def rcv_msg(sck, trg='NET', debug=False):
    r = sck.recv(4096)
    #dprint(r)
    we=ord(targets[trg])
    ml=[]
    for m in split_msgs(r,debug=debug):
        if debug :
            print 'RCV:',
            dprint(m)
        try :
            l,s,d,i,dat=parse_msg(m,debug=debug)
            if trg!='ANY' and d!=we and s!=we:
                #print 'Not for us (%02x): %02x ->  %02x' % (we, s, d)
                continue
            if debug : print_msg(l,s,d,i,dat)
            ml.append([l,s,d,i,dat])
        except IndexError :
            print 'Parse ERROR:',
            dprint(m)
    return ml


def get_pos(sck,trg='ALT'):
    send_cmd(s,trg=trg,cmd='MC_GET_POSITION')
    time.sleep(0.05)
    ml=rcv_msg(s)
    dat='\x00'+ml[-1][-1]
    return struct.unpack('!i',dat)[0]/2**24
    
def get_ver(sck,trg='MB'):
    send_cmd(sck,trg=trg,cmd='GET_VER')
    msg=rcv_msg(sck)[-1]
    return tuple(['%02x' % ord(c) for c in msg[-1]])

def show_pos(sck):
    azm=get_pos(sck,trg='AZM')
    print u'\rAZM %03d°%02d\'%04.1f"' % f2dms(azm),
    time.sleep(0.1)
    alt=get_pos(sck,trg='ALT')
    print u'ALT %+02d°%02d\'%04.1f"' % f2dms(alt),

In [53]:
#create an INET, STREAMing socket
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error:
    print 'Failed to create socket'
    sys.exit()
     
print 'Socket Created'
 
host = '10.24.48.10';
port = 2000;
 
try:
    remote_ip = socket.gethostbyname( host )
 
except socket.gaierror:
    #could not resolve
    print 'Hostname could not be resolved. Exiting'
    sys.exit()
 
#Connect to remote server
s.connect((host , port))
 
print 'Socket Connected to ' + host + ' on ip ' + remote_ip


Socket Created
Socket Connected to 10.24.48.10 on ip 10.24.48.10

In [54]:
send_cmd(s,trg='AZM',cmd='MC_GET_POSITION')
for m in rcv_msg(s,'ANY'):
        print_msg(*m)
        for b in m[-1] : print ('%02x' % ord(b)),
        print
send_cmd(s,trg='ALT',cmd='MC_GET_POSITION')
for m in rcv_msg(s,'ANY'):
        print_msg(*m)
        for b in m[-1] :print ('%02x' % ord(b)),
        print


[3]  HC+ =>  AZM (MC_GET_POSITION):  
[6]  AZM =>  HC+ (MC_GET_POSITION):  266°59'11.6" bd db 6b
[3]  HC+ =>  ALT (MC_GET_POSITION):  
[6]  ALT =>  HC+ (MC_GET_POSITION):  012°42'27.4" 09 09 5a
[3]  HC+ =>  AZM (MC_GET_POSITION):  
[6]  AZM =>  HC+ (MC_GET_POSITION):  266°59'25.7" bd dc 22
[3]  HC+ =>  ALT (MC_GET_POSITION):  
[6]  ALT =>  HC+ (MC_GET_POSITION):  012°42'17.4" 09 08 d8
[3]  HC+ =>  AZM (MC_GET_POSITION):  
[6]  AZM =>  HC+ (MC_GET_POSITION):  266°59'36.7" bd dc b0
[3]  HC+ =>  ALT (MC_GET_POSITION):  
[6]  ALT =>  HC+ (MC_GET_POSITION):  012°42'06.9" 09 08 50
[3]  HC+ =>  AZM (MC_GET_POSITION):  
[6]  AZM =>  HC+ (MC_GET_POSITION):  266°59'48.7" bd dd 4b
[3]  HC+ =>  ALT (MC_GET_POSITION):  
[6]  ALT =>  HC+ (MC_GET_POSITION):  012°41'56.2" 09 07 c6
[3]  HC+ =>  AZM (MC_GET_POSITION):  
[6]  AZM =>  HC+ (MC_GET_POSITION):  267°00'00.8" bd dd e8
[3]  HC+ =>  ALT (MC_GET_POSITION):  
[6]  ALT =>  HC+ (MC_GET_POSITION):  012°41'47.3" 09 07 53
[3]  HC+ =>  AZM (MC_GET_POSITION):  
[6]  AZM =>  HC+ (MC_GET_POSITION):  267°00'13.0" bd de 86
[3]  HC+ =>  ALT (MC_GET_POSITION):  
[6]  ALT =>  HC+ (MC_GET_POSITION):  012°41'36.8" 09 06 ca
[3]  APP =>  AZM (MC_GET_POSITION):  
[6]  AZM =>  APP (MC_GET_POSITION):  267°00'20.4" bd de e6

In [55]:
s.close()

In [ ]:
while True :
    for m in rcv_msg(s,'ANY'):
        print_msg(*m)
        print
    sys.stdout.flush()
    time.sleep(0.05)

In [45]:
struct.unpack('!i','\x00\x00\x0b\xb8')[0]


Out[45]:
3000

In [69]:
send_cmd(s,src='APP',trg='AZM',cmd='GET_VER')
print rcv_msg(s,trg='APP')
time.sleep(0.1)
send_cmd(s,src='APP',trg='ALT',cmd='GET_VER')
print rcv_msg(s,trg='APP')


Parse ERROR: 0x06 0x10 0x0d 0x01 0x47 0xc3
0x3b
Parse ERROR: 0x97
0x3b
Parse ERROR: 0x03 0x0d 0x10 0x01
[]
Parse ERROR: 0x06 0x10 0x0d 0x01 0x48
[]

In [64]:
send_cmd(s,src='APP',trg='AZM',cmd='MC_GET_AUTOGUIDE_RATE')
time.sleep(0.1)
for m in rcv_msg(s,'APP'):
    print_msg(*m)
    print
time.sleep(0.1)    
send_cmd(s,src='APP',trg='AZM',cmd='MC_SET_AUTOGUIDE_RATE',data='\x80')
time.sleep(0.1)
for m in rcv_msg(s,'APP'):
    print_msg(*m)
    print
time.sleep(0.1)    
send_cmd(s,src='APP',trg='AZM',cmd='MC_GET_AUTOGUIDE_RATE')
time.sleep(0.1)
for m in rcv_msg(s,'APP'):
    print_msg(*m)
    print


[3]  APP =>  AZM (MC_GET_AUTOGUIDE_RATE): 

[4] 10 => 20 (f0):  0x47

[4]  APP =>  AZM (MC_SET_AUTOGUIDE_RATE):  0x80

[4] 10 => 20 (f0):  0x46

[3]  APP =>  AZM (MC_GET_AUTOGUIDE_RATE): 

[4] 10 => 20 (f0):  0x47


In [24]:
send_cmd(s,src='NET',trg='HC+',cmd='GET_VER')
rcv_msg(s,trg='ANY')


Out[24]:
[[3, 32, 13, 254, '']]

In [277]:
send_cmd(s,trg='AZM',cmd='MC_GET_POSITION')
rcv_msg(s)
send_cmd(s,trg='ALT',cmd='MC_GET_POSITION')
rcv_msg(s)


Out[277]:
[[3, 32, 17, 1, ''], [6, 17, 32, 1, '\x00\x00\x00']]

In [287]:
print get_ver(s,'HC')


()

In [279]:
send_cmd(s,trg='HC+',cmd='MC_GET_POSITION')
rcv_msg(s)


Out[279]:
[[3, 32, 4, 1, '']]

In [289]:
print get_ver(s,'MB'), get_ver(s,'HC'), get_ver(s,'ALT'), get_ver(s,'AZM')


() () ('07', '0a', '10', '0d') ('07', '0a', '10', '0d')

In [281]:
show_pos(s)
sys.stdout.flush()


AZM 000°00'00.0" ALT +0°00'00.0"

In [284]:
for i in range(10):
    show_pos(s)
    sys.stdout.flush()
    time.sleep(1)


AZM 000°00'00.0" ALT +0°00'00.0"

In [173]:
def goto(s,alt,azm,spd='FAST'):
    al=int(alt*2**24)
    az=int(azm*2**24)
    cmd='MC_GOTO_'+spd
    send_cmd(s,trg='ALT',cmd=cmd,data=struct.pack('!i',al)[1:])
    time.sleep(0.1)
    send_cmd(s,trg='AZM',cmd=cmd,data=struct.pack('!i',az)[1:])

In [241]:
goto(s,0.0,0.0,spd='FAST')

In [199]:
def frac_to_fp24(x):
    return struct.pack('!i',int(abs(x)*2**24))[1:]

def set_guiderate(s,altgr,azmgr):
    ald='POS' if (altgr > 0) else 'NEG'
    azd='POS' if (azmgr > 0) else 'NEG'
    send_cmd(s,trg='ALT',cmd='MC_SET_'+ald+'_GUIDERATE',data=frac_to_fp24(altgr))
    time.sleep(0.05)
    send_cmd(s,trg='AZM',cmd='MC_SET_'+azd+'_GUIDERATE',data=frac_to_fp24(azmgr))    
    time.sleep(0.05)

In [233]:
set_guiderate(s,0,0)

In [240]:
send_cmd(s,trg='ALT',cmd='MC_LEVEL_START')
rcv_msg(s,debug=True)


[3] 20 => 11 (0b): 
[4] 11 => 20 (f0):  0x0b
Out[240]:
[[3, 32, 17, 11, ''], [4, 17, 32, 240, '\x0b']]

In [104]:
struct.unpack('!i','\x00\x02\x7d\xc6')[0]/2**24


Out[104]:
0.009731650352478027

In [96]:
print((3+30/60+12/3600)/360)
for c in struct.pack('f',(3+30/60+12/3600)/360):
    print('%02x' % ord(c))
print()


0.00973148148148
cb
70
1f
3c


In [87]:
''.split()


Out[87]:
[]

In [103]:
a='\x3b1aaaa\x3b2bbbbb\x3b3ccccc'




In [105]:
a.split('\x3b')[1:]


Out[105]:
['1aaaa', '2bbbbb', '3ccccc']

In [ ]:
a.rfind()

In [143]:
split_msgs(a)


Out[143]:
['1aaaa', '2bbbbb', '3cccc']

In [142]:
a


Out[142]:
';1aaaa;2bbbbb;3ccccc'

In [126]:
for c in a:
    print '%02x' % ord(c) ,


3b 31 61 61 61 61 3b 32 62 62 62 62 62 3b 33 63 63 63 63 63

In [ ]: