# Copyright 2005-2008 Nanorex, Inc. See LICENSE file for details.
"""
NanoHiveUtils.py
@author: Brian, Mark
@version: $Id$
@copyright: 2005-2008 Nanorex, Inc. See LICENSE file for details.
History:
Brian wrote the NH_Connection class.
Mark wrote everything else.
Module classification:
Mostly control & io code. Some model & ui code (via assy arg & assy.w).
Probably "simulation". [bruce 071214]
"""
import foundation.env as env, os, sys, time
from platform_dependent.PlatformDependent import find_or_make_Nanorex_subdir
from utilities.prefs_constants import nanohive_path_prefs_key, nanohive_enabled_prefs_key
from PyQt4.Qt import Qt, QApplication, QCursor
from widgets.StatusBar import NanoHiveProgressReporter
def get_nh_simspec_filename(basename):
"""
Return the full path of the Nano-Hive simulation specification file.
"""
if basename:
nhdir = find_or_make_Nanorex_subdir("Nano-Hive")
fn = os.path.normpath(os.path.join(nhdir,str(basename)+"-sim.xml"))
#print "get_nh_simspec_filename(): filename=", fn
return fn
else:
return None
def get_nh_workflow_filename(basename):
"""
Return the full path of the Nano-Hive workflow file.
"""
if basename:
nhdir = find_or_make_Nanorex_subdir("Nano-Hive")
fn = os.path.normpath(os.path.join(nhdir,str(basename)+"-flow.tcl"))
#print "get_nh_workflow_filename(): filename=", fn
return fn
else:
return None
def get_nh_mmp_filename(basename):
"""
Return the full path of the Nano-Hive MMP input file.
"""
if basename:
nhdir = find_or_make_Nanorex_subdir("Nano-Hive")
fn = os.path.normpath(os.path.join(nhdir,str(basename)+".mmp"))
#print "get_nh_mmp_filename(): filename=", fn
return fn
else:
return None
def get_nh_espimage_filename(assy, jigname):
"""
Returns the filename of the ESP Image's png given assy and ESP Image's jigname,
to be stored in the ESP Image's MMP info record.
The filename format is "assyname-jigname.png"
"""
cwd = os.path.join(assy.get_cwd(), assy.name + "-" + jigname + ".png")
return os.path.normpath(cwd)
def get_nh_home_ORIG():
"""
Return the Nano-Hive home directory
"""
nanohive_exe = env.prefs[nanohive_path_prefs_key]
# On Windows, the default location of the Nano-Hive executable is
# C:\Program Files\Nano-Hive\bin\win32-x86\NanoHive.exe
# The home directory is retreived by stripping off the last two directories
# from the Nano-Hive executable path, which is
# C:\Program Files\Nano-Hive
if sys.platform == 'win32':
head, tail = os.path.split(nanohive_exe)
head, tail = os.path.split(head)
nh_home, tail = os.path.split(head)
return nh_home
def get_nh_home():
"""
Returns the Nano-Hive home (base) directory for each platform, if it exists. Otherwise, return None.
"""
if sys.platform == "win32": # Windows
basedir = "C:\Program Files\Nano-Hive"
else: # Linux and MacOS
basedir = "/usr/local/share/Nano-Hive"
if not os.path.exists(basedir): return None
return basedir
def get_nh_config_filename():
"""
Return the full path of the Nano-Hive config.txt file.
"""
if sys.platform == "win32": # Windows
fn = os.path.normpath(get_nh_home() + "/conf/configs.txt")
else: # Linxus and MacOS
fn = os.path.normpath(os.path.expanduser("~/.Nano-Hive/conf/configs.txt"))
if not os.path.exists(fn): return None
return fn
def run_nh_simulation(assy, sim_id, sim_parms, sims_to_run, results_to_save):
"""
Run a Nano-Hive simulation on the part (assy). Only the MPQC_ESP plug-in
used for creating an ESP Image file is supported in A7.
sim_id is the simulation id of the simulation. It is used to construct the
basename of all the simulation files and is the name of the Nano-Hive simulation run.
sims_to_run is a list of simulations to run, where:
MPQC_ESP = MPQC ESP Plane
MPQC_GD = MPQC Gradient Dynamics (not supported in A7)
AIREBO = AIREBO (not supported in A7)
results_to_save is a list of results to save, where:
MEASUREMENTS = Measurements to file
POVRAYVIDEO = POVRay Video
NETCDF = NetCDF
Return values:
0 = successful
1 = Nano-Hive plug-in not enabled
2 = Nano-Hive plug-in path is empty
3 = Nano-Hive plug-in path points to a file that does not exist
4 = Nano-Hive plug-in is not Version 1.2b
5 = Couldn't connect to Nano-Hive instance
6 = "load" command failed
7 = "run" command failed
8 = Simulation aborted
"""
if not sims_to_run:
return # No simulations to run in the list.
# Validate that the Nano-Hive plug-in is enabled.
if not env.prefs[nanohive_enabled_prefs_key]:
r = activate_nh_plugin(assy.w)
if r:
return 1 # Nano-Hive plug-in not enabled.
if not env.prefs[nanohive_path_prefs_key]:
return 2 # Nano-Hive plug-in path is empty
if not os.path.exists(env.prefs[nanohive_path_prefs_key]):
return 3 # Nano-Hive plug-in path points to a file that does not exist
r = verify_nh_program()
if r:
return 4 # Nano-Hive plug-in is not Version 1.2b
if not sim_id:
sim_id = get_sim_id()
output_dir = find_or_make_Nanorex_subdir("Nano-Hive") # ~/Nanorex/Nano-Hive
# Put up the wait cursor. The cursor will be restored by exit_nh().
QApplication.setOverrideCursor( assy.w.ArrowWaitCursor )
# 1. Try to connect to Nano-Hive, get socket.
# 99% of the time, no socket will be returned since Nano-Hive hasn't been started yet.
# Let's check, just in case this is a Nano-Hive instance running.
nh_socket = connect_to_nh()
if nh_socket:
kill_nh = False
else: # No Nano-Hive instance is running. Start it.
kill_nh = True
r = start_nh() # Start Nano-Hive server (instance).
if r:
print "Nano-Hive startup aborted."
# It may take a second or two to connect to the new Nano-Hive instance.
# Keep trying until we get a socket. Give up if we haven't connected within 4 seconds.
start = time.time()
while not nh_socket:
time.sleep(0.25)
nh_socket = connect_to_nh()
duration = time.time() - start
if duration > 4.0: # Give up after 4 seconds
exit_nh(nh_socket, kill_nh) # Only restore's the cursor
return 5 # Couldn't connect to Nano-Hive socket.
# 2. Write the MMP file that Nano-Hive will use for the sim run.
assy.writemmpfile(get_nh_mmp_filename(sim_id))
# 3. Write the sim-spec file using the parameters from the Nano-Hive dialog widgets
from analysis.ESP.files_nh import write_nh_simspec_file
write_nh_simspec_file(sim_id, sim_parms, sims_to_run, results_to_save, output_dir)
# 4. Write the Sim Workflow file
from analysis.ESP.files_nh import write_nh_workflow_file
write_nh_workflow_file(sim_id)
# 5. Send commands to Nano-Hive. There can be no spaces in partname. Need to fix this.
cmd = 'load simulation -f "' + get_nh_simspec_filename(sim_id) + '" -n ' + sim_id
#print "NanoHiveUtils.run_nh_simulation(): N-H load command: ", cmd
success, response = nh_socket.sendCommand(cmd) # Send "load" command.
if not success:
print success, response
exit_nh(nh_socket, kill_nh)
return 6 # "load" command failed
cmd = "run " + sim_id
#print "NanoHiveUtils.run_nh_simulation(): N-H run command: ", cmd
success, response = nh_socket.sendCommand(cmd) # Send "run" command.
if not success:
print success, response
exit_nh(nh_socket, kill_nh)
return 7 # "run" command failed
statusBar = assy.w.statusBar()
progressReporter = NanoHiveProgressReporter(nh_socket, sim_id)
r = statusBar.show_progressbar_and_stop_button(progressReporter, "NanoHive", True)
if r:
stop_nh_sim(sim_id)
exit_nh(nh_socket, kill_nh)
return 8 # simulation aborted
exit_nh(nh_socket, kill_nh)
return 0
def activate_nh_plugin(win):
"""
Opens a message box informing the user that the Nano-Hive plugin
needs to be enabled and asking if they wish to do so.
win is the main window object.
"""
from PyQt4.Qt import QMessageBox
ret = QMessageBox.warning( win, "Activate Nano-Hive Plug-in",
"Nano-Hive plug-in not enabled. Please select OK to \n" \
"activate the Nano-Hive plug-in from the Preferences dialog.",
"&OK", "Cancel", "",
0, 1 )
if ret == 0: # OK
win.userPrefs.showDialog('Plug-ins') # Show Preferences | Plug-in.
if not env.prefs[nanohive_enabled_prefs_key]:
return 1 # Nano-Hive was not enabled by user.
elif ret == 1: # Cancel
return 1
return 0
def verify_nh_program():
"""
Returns 0 if nanohive_path_prefs_key is the path to the Nano-Hive v1.2b executable.
Otherwise, returns 1.
"""
vstring = "Nano-Hive version 1.2.0-beta-1 Copyright (C) 2004,2005 Nano-Hive, LLC"
r = verify_program(env.prefs[nanohive_path_prefs_key], '-v', vstring)
return r
# This is a general function that should be moved to utilities
def verify_program(program, version_flag, vstring):
"""
Verifies a program by running it with the version_flag and matching the output to vstring.
Returns 0 if there is a match. Otherwise, returns 1
"""
if not program:
return 1
if not os.path.exists(program):
return 1
args = [version_flag]
from processes.Process import Process
arguments = []
for arg in args:
if arg != "":
arguments.append(arg)
print
print "arguments:", arguments
p = Process()
p.start(program, arguments)
if not p.waitForFinished (10000): # Wait for 10000 milliseconds = 10 seconds
return 1
output = 'Not vstring'
output = str(p.readAllStandardOutput())
#print "output=", output
#print "vstring=", vstring
if output.find(vstring) == -1:
return 1
else:
return 0 # Match found.
def start_nh():
"""
Starts Nano-Hive server in the background.
Returns 1 if Nano-Hive path is not set or if the path does not exist.
Returns 2 if Nano-Hive config file does not exist.
"""
# Get Nano-Hive executable path from the prefs db.
nanohive_exe = env.prefs[nanohive_path_prefs_key]
if not nanohive_exe:
return 1
if not os.path.exists(nanohive_exe):
return 1
nanohive_config = get_nh_config_filename()
if not nanohive_config:
return 2
args = ['-f', nanohive_config]
#print "start_nh(): args=", args
from processes.Process import Process
arguments = []
for arg in args:
if arg != "":
arguments.append(arg)
nhProcess = Process()
nhProcess.start(nanohive_exe, args)
return 0
def stop_nh_sim(nh_socket, sim_id=None):
"""
Stops a running Nano-Hive simulation.
- the id (name) of the simulator to stop.
"""
if nh_socket:
if sim_id:
success, response = nh_socket.sendCommand("stop " + sim_id) # Send "stop" command.
QApplication.restoreOverrideCursor() # Restore the cursor
def exit_nh(nh_socket, kill_nh):
"""
Exits (kills) the Nano-Hive instance if kill_nh is True,
closes the socket and restores the cursor."
"""
if nh_socket:
if kill_nh:
success, response = nh_socket.sendCommand("exit") # Send "exit" command.
nh_socket.close()
QApplication.restoreOverrideCursor() # Restore the cursor
def connect_to_nh():
"""
Connects to a Nano-Hive instance.
Returns NH_Connection socket if successful, None if failure.
"""
hostIP = "127.0.0.1"
port = 3000 # Nano-Hive 1.2b uses port 3000, not 3002 as 1.2a did. mark 2006-01-04.
serverTimeout = 5.0
clientTimeout = 30.0
method = "NanoHiveUtils.connect_to_nh()"
nh_conn = NH_Connection() # Obtain connection object
# Try connecting the Nano-Hive instance.
success, msg = nh_conn.connect(hostIP, port, serverTimeout, clientTimeout)
if success:
success, result = nh_conn.sendCommand("status 1") # Send status command.
if success:
msg = method + ": Success: " + str(success) + "\nMessage: " + str(result)
#env.history.message(msg)
#print msg
return nh_conn
else:
msg = method + ": Command Error:\nSuccess=" + str(success) + "\nMessage: " + str(result)
#env.history.message(msg)
#print msg
return None
else:
msg = method + ": Connection Failed:\nSuccess=" + str(success) + "\nMessage: " + str(msg)
#env.history.message(msg)
#print msg
return None
# Version 2
import socket
class NH_Connection:
"""
A sockets connection to a Nano-Hive instance.
"""
def connect(self, hostIP, port, serverTimeout, clientTimeout):
"""
Connects to the specified N-H instance.
Parameters:
hostIP
The IP address of the N-H host
port
The port listened to by the SocketsControl plugin of the N-H host
serverTimeout
A float specifying how long, in seconds, to give when reading
from, and writing to, N-H
clientTimeout
A float specifying the timeout period of the SocketsControl
plugin of the N-H host. Not implemented yet, but this will
keep the N-H connection alive with "ping" commands
Returns:
success indicator
A one if the N-H instance was successfully connected to, or a
zero if an error occurred and connection failed
error message
If connection failed, describes the problem
"""
try:
self.connectionHandle = \
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.connectionHandle.connect((hostIP, port))
self.connectionHandle.settimeout(serverTimeout)
except socket.error, errorMessage:
return 0, errorMessage
return 1, ""
def close(self):
"""
Closes the connection to the N-H instance.
"""
self.connectionHandle.close()
def sendCommand(self, command):
"""
Sends a command to the N-H instance and returns the response.
Parameters:
command
The command to send to the N-H instance
Returns:
success indicator
A one if the command was successfully sent and a response was
received
response or error message
If all was successful, the encoded N-H instance response, or a
description of the error if unsuccessful. The encoded response
can be decoded with the
Nano-Hive/data/local/en_resultCodes.txt file
"""
# Send command
success = 1
try:
self.connectionHandle.send(command)
except socket.error, errorMessage:
success = 0
# Read response length
if success:
try:
responseLengthChars = self.connectionHandle.recv(4)
except socket.error, errorMessage:
success = 0
if success:
responseLength = ord(responseLengthChars[0])
responseLength |= ord(responseLengthChars[1]) << 8
responseLength |= ord(responseLengthChars[2]) << 16
responseLength |= ord(responseLengthChars[3]) << 24;
# Read response
try:
response = self.connectionHandle.recv(responseLength)
except socket.error, errorMessage:
success = 0
if success:
return 1, response
else:
return 0, errorMessage