Search code examples
pythonsocketstcp

Trying to get data from a TCP socket connection on a networked device using Python


I am trying to write a simple Python script that connects to a number of Fluke Thermo-Hygrometers (DewK 1620A) and write the temperature and humidity readings to a file I can then ingest into Splunk. This is the first time I've ever tried anything with Python and I'm really close, but can't seem to nail down how to get the data via a TCP socket.

Overview

The script reads a list of devices from an external JSON and then opens a connection to each device, sends a command to get the current readings, and then writes those readings to a file.

In my first iteration, I simply made a single "data = s.recv(64)" call, but the devices were inconsistent in returning a full result. Sometimes I'd get the full result (ie. "69.64,45.9,0,0") and other times I'd only get part of the reading (ie. "69.64,4").

While True Loop

After doing some research, I began to see recommendations for using a while True loop to get the "rest" of the data in a second (or third) pass. I changed out my simple s.recv call with the following:

while True:
     data = s.recv(64)           # Retrieve data from device
     if not data:                # If no data exists, break loop
          continue
     f.write(data.decode())      # Write data to logfile

Unfortunately, now the script will not finish. I suspect the loop is not exiting, but the response should never be more than 24 bytes. I'm not sure how else to test and exit the loop. I tried adding a timeout, but that kills the whole script.

connectDeviceTCP.py

import datetime
import json
import socket

# the command to be passed to the device
command = "read?\r"

# open the deviceLocation.json file for reading
jsonFile = open("devices.json", "r", encoding="utf-8")

# load the contents of the json file into 
locationFile = json.load(jsonFile)
jsonFile.close()

# for each device, connect & retrieve device readings
for device in locationFile["devices"]:

    location = device["location"]
    host = device["host"]
    portStr = device["port"]

    # convert the portStr variable from string to integer
    port = int(portStr)
    
    # open log file for appending.
    f = open("log/" + location + ".log", "a")

    try:

        # create a socket on client using TCP/IP
        s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        
        # set socket timeout to 3 seconds
        s.settimeout(10)
        
        # connect to host and port
        s.connect((host, port))

    except TimeoutError:

        # write to the log file if the host cannot be reached
        f.write(('\n{:%Y-%m-%d %H:%M:%S} - '.format(datetime.datetime.now())) + location + " - Error: " + host + ":" + portStr + " does not respond.")

    else:

        # send the command to the device
        s.sendall(command.encode())

        # write the timestamp and location to the log
        f.write(('{:%Y-%m-%d %H:%M:%S} - '.format(datetime.datetime.now())) + location + " - ")

        while True:
            data = s.recv(64)           # Retrieve data from device
            if not data:                # If no data exists, break loop
                break
            f.write(data.decode())      # Write data to logfile
        
        # --- The following work except the device doesn't always spit out all the data.
        # receive message string from device
        #data = s.recv(64)

        # write returned values to log file.
        #f.write(data.decode())

    finally:

        # disconnect the client
        s.close()      

        # close log file
        f.close()

Solution

  • The devices communicate line-oriented with lines terminated by \r, so just read a whole line - replace the while True loop with

            data = s.makefile(newline='\r').readline()
            f.write(data)      # Write data to logfile