Using Python math trigonometry functions in Client Event scripts

I have a Python script set up in the Client Events section of the Vision module, where I would like to make use of the math.sin(), math.cos() and math.atan2() functions to process some vector data stored in a database into a client dataset memory tag and display it to a page. I have been encountering an error “NameError: global name ‘math’ is not defined” where the client is unable to run the script despite having “import math” in the first line. This leads me to believe that clients are unable to use the default Python modules.

Is it possible to install Python’s included Math module to the gateway such that all clients are able to use these functions? The Project Library seems like a lead but I am unsure how to go about this. I have seen a few other threads that mention the Apache Math functions, however these are only for the Jython/Java environment, correct?

Welcome to the forum!

I don’t know for sure, but I imagine the response you’re going to get is;

… consider a re-think of your design approach. Let the server (gateway) do the number crunching, pulling the information from the DB, processing, and then serving the information to the relevant client(s)…

What does your script look like? Does it work in the Script Console?

Something simple like this works for me:

import math

print math.floor(0.5)

Yeah, that code in the Script Console works fine for me too. I can run my script with no errors by pasting it into the Console in the Designer, but as soon as it’s in the Client Events area, I get the issue.

There’s a bit to it but I’ve attached a stripped-down version of the script with the relevant sections. You should be able to put an arbitrary array of angles in degrees into the function provided here.

import math

def averageAngles(angle_array):
	"""
	Takes array of angles in degrees and finds the average. Each angle is used to create a unit vector with x and y 
	components using cosine and sine definitions. Components are then summed and divided by the number of angles to 
	produce x' and y'. Average coordinates are then used to calculate the average angle using the atan2() method, a 
	version of arctan / inverse tangent which preserves angle sign across all four quadrants.  
	
	Inputs:
		angle_array (array): One-dimensional array containing angles (in degrees) to be averaged 
		
	Returns: 
		angle_prime (float): Average angle calculated from array. Angles are given between 0 and 360 degrees, with a 
							 value of -1 indicating a "None" angle in the situation where the net vector is null
	"""
	
	angle_count = 0
	x_prime = 0
	y_prime = 0
	
	for angle in angle_array:
		if angle != "None" or angle != -1:
			# Convert to radians, find x and y components for an angle defined from North heading
			theta = math.radians(angle)
			x = math.cos(theta)
			y = math.sin(theta)
		
			# Add new x and y to x' and y' accumulators 
			x_prime += x
			y_prime += y
	
		# Increase angle count
		angle_count += 1 
		
	# Check if angle_array is empty or angle is null condition
	if angle_count != 0 and (x_prime != 0 and y_prime != 0):
		# Divide accumulators by number of angles to find average 
		x_prime = x_prime / angle_count
		y_prime = y_prime / angle_count
		
		# Calculate inverse angle from average x and y positions 
		theta_prime = math.atan2(y_prime, x_prime)
		angle_prime = math.degrees(theta_prime)
		
		# Bound angle to positive conditions between 0 and 360
		if angle_prime < 0:
			angle_prime += 360
		
		# Return calculated angle	
		return angle_prime 
	else: 
		# Null condition
		return -1

#
# date = system.tag.read("[Client]date_tagpath").value 
# Populate day[] with shift timestamps from date
#

day_angles = []
for shift in day: # Day is array of relevant timestamps
    #
    # Code to fetch dataset from database here. Create shift_data array of angles in degrees eg. [45, 90, 270, 340]
    #

    shift_angles = averageAngles(shift_data)
    day_angles.append(shift_angles)

#
# Create new dataset from day_angles
# system.tag.write("[Client]dataset_tagpath", dataset)
#

What happens if you move import math into the average_angles() function?

3 Likes

What Jordan is hinting at is that event scripts have a legacy scope behavior for top-level names that prevents imports at the top level from being available within further-nested function scopes. (A backward compatibility mess that I wish IA had broken at v8.0…)

You should treat any def buried in an event script to be a “There Be Dragons” warning flare. Unless you have a very specific reason (closures, perhaps), you should put such functions in project scripts. And call them from the event script. Project scripts have modern top-level scope rules.

4 Likes

You can always use the taylor power series to calculate sin

x = .5
numOfterms=100
sin_of_x = sum([ (-1)**(n+1) * x**(2n-1) / fact(2n-1) for n in range(1, numOfTerms)])

Just kidding don’t do this. Do what @pturmel said.

4 Likes

You know that’ll get a like from me. :blush:

1 Like