Hexadecimal binary socket transfer

I have the following code to save data (to sd card on machine) and get a hex binary file from that card into my Ignition server. Has anyone worked with binary files in python before that could help me with this? The code returns only the first (packet) of the data, I know this because it should start with 0c and end with 0c but returns something like this in script console:

'SAVE D 1 Y’response: ‘*OK\r’
‘\x0c1200300\x0c\x05\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x10\x00\x00\x00…\x00\x13\x00\x13\x00\x00\x00’

I was instructed by the manufacturer that I need to store the data in a buffer and wait until its all in then I can parse the values (I need to discard the first 200 bytes and loop through all remaining bytes 8 bytes at a time to then convert the first 6 of 8 and use those integer values in my script)

import system, math, struct
from socket import socket

def recvcmd(sockSend, sockRecv, cmd):
    sockSend.send('-' + cmd + '\r')
    data = sockRecv.recv(300000000)
    #print struct.unpack("!B", data[0]) # (note 2)
    print repr(data)

def sendcmd(sock, cmd):
    sock.send('-' + cmd + '\r')
    ack = sock.recv(3)
    if ack != '*!\r':
        print((repr(cmd) + 'Bad ack: ' + repr(ack)))
    while True:
        resp = sock.recv(100)
        print((repr(cmd) + 'response: ' + repr(resp)))
        if resp.find('*OK\r') != -1:
            break
        elif resp.find('*FAIL') != -1:
            error = int(resp[6:])
            error_str = resp.strip() + ', ' + error_msgs[error]
            raise Exception('command ' + repr(cmd) + ': ' + error_str)
            break

# a simple uniwest ethernet protocol command line sender

ip_addr = '192.168.10.235'
command1 = 'SAVE D 1 Y'
command2 = 'GETFILE D 1'


sockSend = socket()
sockSend.connect((ip_addr, 55556))

sockRecv = socket()
sockRecv.connect((ip_addr, 55555))

sendcmd(sockSend, command1)
recvcmd(sockSend, sockRecv, command2)

I’m pretty sure you’re going to suffer as long as you are trying to handle 8-bit traffic with jython’s java-based 16-bit character strings. Start here, and work exclusively with java byte arrays.

I am able to get the data from the individual packets like this:

def recvcmd(sockSend, sockRecv, cmd):
    sockSend.send('-' + cmd + '\r')
    sockRecv.settimeout(5)
    loopGo = 1
    data = sockRecv.recv(300000000)
    print len(data)
    while loopGo:
    	try:
    		newData = sockRecv.recv(300000000)
    		data = data + newData
    		print len(data)
    		if len(data) == 1200310:
    			loopGo = 0
    			print len(data)
    			#data.seek(0)  # Go to beginning
    			#couple_bytes = data.read(301)
    			#print couple_bytes
    	except sockRecv.Timeouterror:
    		break

but haven’t used byte array before and not sure how to implement that in this code.
Rather than looping, maybe I can use ByteArrayInputStream(bytes) to get all the data at once without a loop?

I would process it as it comes in, wrapping the socket input stream in a DataInputStream. Then you can read the shorts directly, and skip shorts.

I have made some progress with this but sometimes the port may be busy while I run the script and in that case I get some runaway runnable async threads which are killing my processor until I have to restart the gateway to clear them.
I am looking into making threads interruptable but don’t know if its necessary or how to do that with the python module I have.

Updated code - seems to hang up during an exception

def getBit(i, n):
	return (i >> n) & 1

# a simple uniwest ethernet protocol command line sender
def sendcmd(sock, cmd):
	sock.send('-' + cmd + '\r')
	ack = sock.recv(3)
	if ack != '*!\r':
		print((repr(cmd) + 'Bad ack: ' + repr(ack)))
	while True:
		resp = sock.recv(100)
		print((repr(cmd) + 'response: ' + repr(resp)))
		if resp.find('*OK\r') != -1:
			break
		elif resp.find('*FAIL') != -1:
			error = int(resp[6:])
			error_str = resp.strip() + ', ' + error_msgs[error]
			raise Exception('command ' + repr(cmd) + ': ' + error_str)
			sock.close()
			break

def senddata(ip_addr):
	command1 = 'GAIN 55'
	command2 = 'DATASTART'
	
	sockSend = socket.socket()
	sockSend.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
	sockSend.connect((ip_addr, 55556))
	sendcmd(sockSend, command1)
	sockSend.close()


def processdata():
	logger = system.util.logger("Eddyview")
	#Read integer: convert to binary, and get selected eddyview information
	target = system.tag.read("System/System Variables/EddyviewSave").value
	if getBit(target, 0) == 1:
		ip_addr = "192.168.10.70"
		line = "CTL3"
		tag = "WIC/CTL3/Eddyview/Voltage"
		system.tag.write("System/System Variables/EddyviewRead", 0)
		length = system.tag.read("WIC/CTL3/Taymer/ActualLength Absolute").value
	elif getBit(target, 1) == 1:
		ip_addr = "192.168.10.78"
		line = "CTL4"
		tag = "WIC/CTL25/Eddyview/Voltage"
		system.tag.write("System/System Variables/EddyviewRead 1", 0)
		length = system.tag.read("WIC/CTL3/Taymer/ActualLength Absolute").value
	elif getBit(target, 2) == 1:
		ip_addr = "192.168.10.235"
		line = "CTL5"
		tag = "WIC/CTL25/Eddyview/Voltage"
		system.tag.write("System/System Variables/EddyviewRead 2", 0)
		length = system.tag.read("WIC/CTL3/Taymer/ActualLength Absolute").value
	elif getBit(target, 3) == 1:
		ip_addr = "192.168.10.235"
		line = "CTL5"
		tag = "WIC/CTL25/Eddyview/Voltage"
		system.tag.write("System/System Variables/EddyviewRead 3", 0)
		length = system.tag.read("WIC/CTL3/Taymer/ActualLength Absolute").value
	elif getBit(target, 4) == 1:
		ip_addr = "192.168.10.235"
		line = "CTL5"
		tag = "WIC/CTL25/Eddyview/Voltage"
		system.tag.write("System/System Variables/EddyviewRead 4", 0)
		length = system.tag.read("WIC/CTL3/Taymer/ActualLength Absolute").value
	elif getBit(target, 5) == 1:
		ip_addr = "192.168.10.235"
		line = "CTL5"
		tag = "WIC/CTL25/Eddyview/Voltage"
		system.tag.write("System/System Variables/EddyviewRead 5", 0)
		length = system.tag.read("WIC/CTL3/Taymer/ActualLength Absolute").value	
	elif getBit(target, 6) == 1:
		ip_addr = "192.168.10.235"
		line = "CTL5"
		tag = "WIC/CTL25/Eddyview/Voltage"
		system.tag.write("System/System Variables/EddyviewRead 6", 0)
		length = system.tag.read("WIC/CTL3/Taymer/ActualLength Absolute").value
	elif getBit(target, 7) == 1:
		ip_addr = "192.168.10.235"
		line = "CTL5"
		tag = "WIC/CTL25/Eddyview/Voltage"
		system.tag.write("System/System Variables/EddyviewRead 7", 0)
		length = system.tag.read("WIC/CTL3/Taymer/ActualLength Absolute").value	
	else:
		ip_addr = ""
		line = "None"
		tag = ""
		length = 101

	#Fetch and process a lot of data. Write parameter data to tags
	loopCount = 0
	complete = 0
	BUFFER_SIZE = 1024
	command1 = 'SAVE D 1 Y'
	command2 = 'GETFILE D 1'

	def recvcmd():
		try:
			sockSend.send('-' + command2 + '\r')
			#sockRecv.settimeout(10)
			data = ""
			ack = sockSend.recv(3)
			#print((repr(command2) + 'Ack: ' + repr(ack)))
			runnin = 0
			while not data.endswith("\x0c") or runnin ==0: # happens when response doesn't fit in buffer
				data += sockRecv.recv(BUFFER_SIZE) #append to list and join together as a string
				if len(data) > 1:
					runnin = 1
			#print repr(data[0])
			going = 1
			counter = 309
			magnitude = 0
			while going == 1:
				try:
					valueV = struct.unpack("<h", data[counter]+data[counter + 1])[0]
					voltsV = valueV * 0.000436401
					valueH = struct.unpack("<h", data[counter + 2]+data[counter + 3])[0]
					voltsH = valueH * 0.000436401
					counter += 14
					currentMagnitude = math.sqrt(voltsV * voltsV + voltsH * voltsH)
					if currentMagnitude > magnitude:
						magnitude = currentMagnitude
						v = voltsV
						h = voltsH
				except:
					going = 0
					#sockSend.close()
					#sockRecv.close()
			#sockSend.close()
			#sockRecv.close()
			if magnitude > 0:
				#phase calculation
				angle = math.degrees(math.atan2(v, h)) % 360
				#system.tag.write("WIC/CTL3/Eddyview/v", v)
				#system.tag.write("WIC/CTL3/Eddyview/h", h)
				system.tag.write(tag, magnitude)
				#system.tag.write("WIC/CTL3/Eddyview/angle", angle)
				system.db.runPrepUpdate("INSERT INTO EddyCurrentLog (Length,HorizValue,VertValue,Amplitude,Phase, Voltage, Line, LineRunning) VALUES (?,?,?,?,?,?,?,?)", 
				 [length, h, v, magnitude,angle, magnitude, line, 0], "SQLServerluvatawaterbury")
				return 1

		except:
			return 0
			
	while loopCount < 5 and len(ip_addr) > 0:
		loopCount += 1
		logger.info("loop Count: " + str(loopCount))
		#system.tag.write("System/System Variables/EddyviewLoop", loopCount)
		if complete == 1:
			break
		else:
			try:
				sockSend = socket.socket()
				sockSend.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
				sockSend.settimeout(10) #was 10
			
				sockRecv = socket.socket()
				sockRecv.settimeout(10) #was 10
				sockRecv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

				#cooldown timer try was here
				sockSend.connect((ip_addr, 55556))
				sockRecv.connect((ip_addr, 55555))
				#Save
				sendcmd(sockSend, command1)
				complete = recvcmd()
				logger.info("Sent GetFile")
				sockSend.close()
				sockRecv.close()
			except:
				sockSend.close()
				sockRecv.close()
				logger.info("Exception: Close sockets")
			time.sleep(1)
			

Consider using the techniques shown in clientserial.py, from this thread, keeping the reading thread separate from the state machine that sends commands. The code should work well with a Java socket and a DataInputStream instead of a serial port.