def run(self): # Try to connect to an incoming tcp socket using its socket descriptor tcpSocket = QTcpSocket() if not tcpSocket.setSocketDescriptor(self.socketDescriptor): FreeCAD.Console.PrintError("Socket not accepted.\n") return FreeCAD.Console.PrintLog("Socket accepted.\n") # Wait for an incoming message if not tcpSocket.waitForReadyRead(msecs=WAIT_TIME_MS): FreeCAD.Console.PrintError("No request send.\n") return # Make an input data stream instr = QDataStream(tcpSocket) instr.setVersion(QDataStream.Qt_4_0) # Try to read the message size if self.blockSize == 0: if tcpSocket.bytesAvailable() < 2: FreeCAD.Console.PrintError("Received message " + "has too few bytes.\n") return self.blockSize = instr.readUInt16() # Check message is sent complete if tcpSocket.bytesAvailable() < self.blockSize: FreeCAD.Console.PrintError("Received message has less bytes " + "then it's supposed to.\n") return # Read message and inform about it cmd = instr.readRawData(self.blockSize).decode("UTF-8") FreeCAD.Console.PrintLog("CommandServer received> " + cmd + "\n") # Try to execute the message string and prepare a response try: exec(cmd) except Exception as e: FreeCAD.Console.PrintError("Executing external command failed:" + str(e) + "\n") message = "Command failed - " + str(e) else: FreeCAD.Console.PrintLog("Executing external command succeeded!\n") message = COMMAND_EXECUTED_CONFIRMATION_MESSAGE # Prepare the data block to send back and inform about it FreeCAD.Console.PrintLog("CommandServer sending> " + message + " \n") block = QByteArray( len(message.encode("UTF-8")).to_bytes(2, byteorder='big') + message.encode("UTF-8")) outstr = QDataStream(block, QIODevice.WriteOnly) outstr.setVersion(QDataStream.Qt_4_0) # Send the block, disconnect from the socket and terminate the QThread tcpSocket.write(block) tcpSocket.disconnectFromHost() tcpSocket.waitForDisconnected()
def run(self): # Try to connect to an incomming tcp socket using its socket descriptor tcpSocket = QTcpSocket() if not tcpSocket.setSocketDescriptor(self.socketDescriptor): FreeCAD.Console.PrintError("Socket not accepted.\n") return # Wait for an incomming message if not tcpSocket.waitForReadyRead(msecs=WAIT_TIME_MS): FreeCAD.Console.PrintError("No request send.\n") return # Make an input data stream instr = QDataStream(tcpSocket) instr.setVersion(QDataStream.Qt_4_0) # Try to read the message size if self.blockSize == 0: if tcpSocket.bytesAvailable() < 2: return self.blockSize = instr.readUInt16() # Check message is sent complete if tcpSocket.bytesAvailable() < self.blockSize: return # Read message and inform about it instr = instr.readString() FreeCAD.Console.PrintLog("CommandServer received> " + str(instr) + "\n") # Try to execute the message string and prepare a response try: exec(str(instr)) except Exception as e: FreeCAD.Console.PrintError("Executing external command failed:" + str(e) + "\n") message = "Command failed - " + str(e) else: FreeCAD.Console.PrintLog("Executing external command succeded!\n") message = COMMAND_EXECUTED_CONFIRMATION_MESSAGE # Prepare the data block to send back and inform about it FreeCAD.Console.PrintLog("CommandServer sending> " + message + " \n") block = QByteArray() outstr = QDataStream(block, QIODevice.WriteOnly) outstr.setVersion(QDataStream.Qt_4_0) outstr.writeUInt16(0) outstr.writeQString(message) outstr.device().seek(0) outstr.writeUInt16(block.size() - 2) # Send the block, disconnect from the socket and terminate the QThread tcpSocket.write(block) tcpSocket.disconnectFromHost() tcpSocket.waitForDisconnected()
def run(self): tcpSocket = QTcpSocket() if not tcpSocket.setSocketDescriptor(self.socketDescriptor): self.error.emit(tcpSocket.error()) return block = QByteArray() outstr = QDataStream(block, QIODevice.WriteOnly) outstr.setVersion(QDataStream.Qt_4_0) outstr.writeUInt16(0) outstr.writeQString(self.text) outstr.device().seek(0) outstr.writeUInt16(block.size() - 2) tcpSocket.write(block) tcpSocket.disconnectFromHost() tcpSocket.waitForDisconnected()
def sendClientCommand(host, port, cmd, wait_time=WAIT_TIME_MS): # Try to connect to a host server tcpSocket = QTcpSocket() tcpSocket.connectToHost(host, port, QIODevice.ReadWrite) if not tcpSocket.waitForConnected(msecs=wait_time): return CLIENT_ERROR_NO_CONNECTION # Prepare a command message to be sent block = QByteArray() outstr = QDataStream(block, QIODevice.WriteOnly) outstr.setVersion(QDataStream.Qt_4_0) outstr.writeUInt16(0) outstr.writeQString(cmd) outstr.device().seek(0) outstr.writeUInt16(block.size() - 2) tcpSocket.write(block) # Try to send the message if not tcpSocket.waitForBytesWritten(msecs=wait_time): return CLIENT_ERROR_BLOCK_NOT_WRITTEN # Wait for a response from the host server if not tcpSocket.waitForReadyRead(msecs=10000): return CLIENT_ERROR_NO_RESPONSE # Try to read the response instr = QDataStream(tcpSocket) instr.setVersion(QDataStream.Qt_4_0) blockSize = 0 if blockSize == 0: if tcpSocket.bytesAvailable() < 2: return CLIENT_ERROR_RESPONSE_NOT_COMPLETE blockSize = instr.readUInt16() if tcpSocket.bytesAvailable() < blockSize: return CLIENT_ERROR_RESPONSE_NOT_COMPLETE # Wait until the host server terminates the connection tcpSocket.waitForDisconnected() # Return value representing a command execution status if instr.readString() == COMMAND_EXECUTED_CONFIRMATION_MESSAGE: return CLIENT_COMMAND_EXECUTED else: return CLIENT_COMMAND_FAILED
class CommandClient: ## @property host # A QtHostAddress to the `CommandServer`. ## @property port # An int of port at which `CommandServer` is listening. ## @property tcpSocket # A QTcpSocket used to contact `CommandSErver` ## @property blockSize # An int representing size of incomming tcp message. ## @brief Initialization method for CommandClient. # #A class instance is created and its attributes are initialized. # # # @param host A QtHostAddress to the `CommandServer`. # @param port An int of port at which `CommandServer` is listening. # def __init__(self, host, port): self.host = host self.port = port self.tcpSocket = QTcpSocket() self.blockSize = 0 ## @brief Method used to send commands from client to `CommandServer`. # #This method tries to connect to a specified host `CommandServer` via #`tcpSocket`. If connection was successfull, the command `cmd` is sent. #Then the response is expected. If the response is equal to #COMMAND_EXECUTED_CONFIRMATION_MESSAGE, then the execution was successfull. #The progress and result of `sendCommand` can be obtained from printed logs and #return value. # # # @param cmd A str command to be executed. # # @return # `CLIENT_COMMAND_EXECUTED` if all went great and command was executed. # `CLIENT_COMMAND_FAILED` if `cmd` execution failed. # `CLIENT_ERROR_RESPONSE_NOT_COMPLETE` if a response received was incomplete. # `CLIENT_ERROR_NO_RESPONSE` if there was no response within `WAIT_TIME_MS`. # `CLIENT_ERROR_BLOCK_NOT_WRITTEN` if communication failed during sending. # `CLIENT_ERROR_NO_CONNECTION` if no connection to a host was established. # def sendCommand(self, cmd): # connect a Qt slot to receive and print errors self.tcpSocket.error.connect(self.displayError) # Try to connect to a host server self.tcpSocket.connectToHost(self.host, self.port, QIODevice.ReadWrite) if not self.tcpSocket.waitForConnected(msecs=WAIT_TIME_MS): if "FreeCAD" in sys.modules: FreeCAD.Console.PrintError( "CommandClient.sendCommand error: " + "No connection\n") else: print("CommandClient.sendCommand error: No connection\n") return CLIENT_ERROR_NO_CONNECTION # Prepare a command message to be sent block = QByteArray() outstr = QDataStream(block, QIODevice.WriteOnly) outstr.setVersion(QDataStream.Qt_4_0) outstr.writeUInt16(0) outstr.writeQString(cmd) outstr.device().seek(0) outstr.writeUInt16(block.size() - 2) # Try to send the message if "FreeCAD" in sys.modules: FreeCAD.Console.PrintMessage("CommandClient sending> " + cmd + "\n") else: print("CommandClient sending> " + cmd + "\n") self.tcpSocket.write(block) if not self.tcpSocket.waitForBytesWritten(msecs=WAIT_TIME_MS): if "FreeCAD" in sys.modules: FreeCAD.Console.PrintError( "CommandClient.sendCommand error: " + "Block not written\n") else: print("CommandClient.sendCommand error: Block not written\n") return CLIENT_ERROR_BLOCK_NOT_WRITTEN # Wait for a response from the host server if not self.tcpSocket.waitForReadyRead(msecs=WAIT_TIME_MS): if "FreeCAD" in sys.modules: FreeCAD.Console.PrintError( "CommandClient.sendCommand error: " + "No response received.\n") else: print("CommandClient.sendCommand error: " + "No response received.\n") return CLIENT_ERROR_NO_RESPONSE # Try to read the response instr = QDataStream(self.tcpSocket) instr.setVersion(QDataStream.Qt_4_0) if self.blockSize == 0: if self.tcpSocket.bytesAvailable() < 2: return CLIENT_ERROR_RESPONSE_NOT_COMPLETE self.blockSize = instr.readUInt16() if self.tcpSocket.bytesAvailable() < self.blockSize: return CLIENT_ERROR_RESPONSE_NOT_COMPLETE response = instr.readString() if "FreeCAD" in sys.modules: FreeCAD.Console.PrintMessage("CommandClient received> " + response + "\n") else: print("CommandClient received> " + response + "\n") # Wait until the host server terminates the connection self.tcpSocket.waitForDisconnected() # Reset blockSize to prepare for sending next command self.blockSize = 0 # Return value representing a command execution status if response == COMMAND_EXECUTED_CONFIRMATION_MESSAGE: return CLIENT_COMMAND_EXECUTED else: return CLIENT_COMMAND_FAILED ## @brief `Qt`'s slot method to print out received `tcpSocket`'s error. # #QAbstractSocket.RemoteHostClosedError is not printed, because it occurs #naturaly when the `tcpSocket` closes after a transaction is over. Except that #all errors are printed. # # # @param socketError A QAbstractSocket::SocketError enum describing occured error. # def displayError(self, socketError): if socketError != QAbstractSocket.RemoteHostClosedError: if "FreeCAD" in sys.modules: FreeCAD.Console.PrintError( "CommandClient error occurred> %s." % self.tcpSocket.errorString() + "\n") else: print("CommandClient error occurred> %s." % self.tcpSocket.errorString() + "\n")
def sendClientCommand(host, port, cmd, wait_time=WAIT_TIME_MS): """ Method to be used for sending commands. This method is an alternative to using `CommandClient`. It does not print any logs, just returns a value saying how the execution went. To send a command using this method do: sendClientCommand("127.0.0.1",54333, "FreeCAD.Console.PrintWarning('Hello World\\n')") Args: cmd: A str command to be executed. host: A QtHostAddress to the `CommandServer`. port: An int of port at which `CommandServer` is listening. Kwargs: wait_time: An int setting milliseconds to wait for connection or message. Returns: `CLIENT_COMMAND_EXECUTED` if all went great and command was executed. `CLIENT_COMMAND_FAILED` if `cmd` execution failed. `CLIENT_ERROR_RESPONSE_NOT_COMPLETE` if a response received was incomplete. `CLIENT_ERROR_NO_RESPONSE` if there was no response within `WAIT_TIME_MS`. `CLIENT_ERROR_BLOCK_NOT_WRITTEN` if communication failed during sending. `CLIENT_ERROR_NO_CONNECTION` if no connection to a host was established. """ # Try to connect to a host server tcpSocket = QTcpSocket() tcpSocket.connectToHost(host, port, QIODevice.ReadWrite) if not tcpSocket.waitForConnected(msecs=wait_time): return CLIENT_ERROR_NO_CONNECTION # Prepare a command message to be sent block = QByteArray( len(cmd.encode("UTF-8")).to_bytes(2, byteorder='big') + cmd.encode("UTF-8")) outstr = QDataStream(block, QIODevice.WriteOnly) outstr.setVersion(QDataStream.Qt_4_0) tcpSocket.write(block) # Try to send the message if not tcpSocket.waitForBytesWritten(msecs=wait_time): return CLIENT_ERROR_BLOCK_NOT_WRITTEN # Wait for a response from the host server if not tcpSocket.waitForReadyRead(msecs=wait_time): return CLIENT_ERROR_NO_RESPONSE # Try to read the response instr = QDataStream(tcpSocket) instr.setVersion(QDataStream.Qt_4_0) blockSize = 0 if blockSize == 0: if tcpSocket.bytesAvailable() < 2: return CLIENT_ERROR_RESPONSE_NOT_COMPLETE blockSize = instr.readUInt16() if tcpSocket.bytesAvailable() < blockSize: return CLIENT_ERROR_RESPONSE_NOT_COMPLETE # Wait until the host server terminates the connection tcpSocket.waitForDisconnected() # Return value representing a command execution status if instr.readRawData(blockSize).decode("UTF-8") \ == COMMAND_EXECUTED_CONFIRMATION_MESSAGE: return CLIENT_COMMAND_EXECUTED else: return CLIENT_COMMAND_FAILED
class CommandClient: """ Class to be used for sending commands. This class can be used in FreeCAD's or regular python console to send commands to a `CommandServer` using `sendCommand()`. The class prints logs as it moves along. Attributes: host: A QtHostAddress to the `CommandServer`. port: An int of port at which `CommandServer` is listening. tcpSocket: A QTcpSocket used to contact `CommandSErver` blockSize: An int representing size of incoming tcp message. To send a commands do: client = CommandClient("127.0.0.1",54321) client.sendCommand('FreeCAD.Console.PrintError("Hello World\\n")\n') client.sendCommand('FreeCAD.Console.PrintError("Bye Bye\\n")\n') """ def __init__(self, host, port): """ Initialization method for CommandClient. A class instance is created and its attributes are initialized. Args: host: A QtHostAddress to the `CommandServer`. port: An int of port at which `CommandServer` is listening. """ self.host = host self.port = port self.tcpSocket = QTcpSocket() self.blockSize = 0 def sendCommand(self, cmd): """ Method used to send commands from client to `CommandServer`. This method tries to connect to a specified host `CommandServer` via `tcpSocket`. If connection was successful, the command `cmd` is sent. Then the response is expected. If the response is equal to COMMAND_EXECUTED_CONFIRMATION_MESSAGE, then the execution was successful. The progress and result of `sendCommand` can be obtained from printed logs and return value. Args: cmd: A str command to be executed. Returns: `CLIENT_COMMAND_EXECUTED` if all went great and command was executed. `CLIENT_COMMAND_FAILED` if `cmd` execution failed. `CLIENT_ERROR_RESPONSE_NOT_COMPLETE` if a response received was incomplete. `CLIENT_ERROR_NO_RESPONSE` if there was no response within `WAIT_TIME_MS`. `CLIENT_ERROR_BLOCK_NOT_WRITTEN` if communication failed during sending. `CLIENT_ERROR_NO_CONNECTION` if no connection to a host was established. """ # connect a Qt slot to receive and print errors self.tcpSocket.error.connect(self.displayError) # Try to connect to a host server self.tcpSocket.connectToHost(self.host, self.port, QIODevice.ReadWrite) if not self.tcpSocket.waitForConnected(msecs=WAIT_TIME_MS): if "FreeCAD" in sys.modules: FreeCAD.Console.PrintError( "CommandClient.sendCommand error: " + "No connection\n") else: print("CommandClient.sendCommand error: No connection\n") return CLIENT_ERROR_NO_CONNECTION # Prepare a command message to be sent block = QByteArray( len(cmd.encode("UTF-8")).to_bytes(2, byteorder='big') + cmd.encode("UTF-8")) outstr = QDataStream(block, QIODevice.WriteOnly) outstr.setVersion(QDataStream.Qt_4_0) # Try to send the message if "FreeCAD" in sys.modules: FreeCAD.Console.PrintMessage("CommandClient sending> " + cmd + "\n") else: print("CommandClient sending> " + cmd + "\n") self.tcpSocket.write(block) if not self.tcpSocket.waitForBytesWritten(msecs=WAIT_TIME_MS): if "FreeCAD" in sys.modules: FreeCAD.Console.PrintError( "CommandClient.sendCommand error: " + "Block not written\n") else: print("CommandClient.sendCommand error: Block not written\n") return CLIENT_ERROR_BLOCK_NOT_WRITTEN # Wait for a response from the host server if not self.tcpSocket.waitForReadyRead(msecs=WAIT_TIME_MS): if "FreeCAD" in sys.modules: FreeCAD.Console.PrintError( "CommandClient.sendCommand error: " + "No response received.\n") else: print("CommandClient.sendCommand error: " + "No response received.\n") return CLIENT_ERROR_NO_RESPONSE # Try to read the response instr = QDataStream(self.tcpSocket) instr.setVersion(QDataStream.Qt_4_0) if self.blockSize == 0: if self.tcpSocket.bytesAvailable() < 2: return CLIENT_ERROR_RESPONSE_NOT_COMPLETE self.blockSize = instr.readUInt16() if self.tcpSocket.bytesAvailable() < self.blockSize: return CLIENT_ERROR_RESPONSE_NOT_COMPLETE response = instr.readRawData(self.blockSize).decode("UTF-8") if "FreeCAD" in sys.modules: FreeCAD.Console.PrintMessage("CommandClient received> " + response + "\n") else: print("CommandClient received> " + response + "\n") # Wait until the host server terminates the connection self.tcpSocket.waitForDisconnected() # Reset blockSize to prepare for sending next command self.blockSize = 0 # Return value representing a command execution status if response == COMMAND_EXECUTED_CONFIRMATION_MESSAGE: return CLIENT_COMMAND_EXECUTED else: return CLIENT_COMMAND_FAILED def displayError(self, socketError): """ `Qt`'s slot method to print out received `tcpSocket`'s error. QAbstractSocket.RemoteHostClosedError is not printed, because it occurs naturally when the `tcpSocket` closes after a transaction is over. Except that all errors are printed. Args: socketError: A QAbstractSocket::SocketError enum describing occurred error. """ if socketError != QAbstractSocket.RemoteHostClosedError: if "FreeCAD" in sys.modules: FreeCAD.Console.PrintError( "CommandClient error occurred> %s." % self.tcpSocket.errorString() + "\n") else: print("CommandClient error occurred> %s." % self.tcpSocket.errorString() + "\n")
def run(self): """ Thread's functionality method. The starting point for the thread. After calling start(), the newly created thread calls this function. This function then tries to make QTcpSocket. It waits `WAIT_TIME_MS` for an incoming message. If message is received it checks its a whole message using blockSize sent in the first word as an UINT16 number. If a whole message is received, the thread tries to execute the message string and sends back an appropriate response. The response is *Command failed - "error string"* if the execution failed, or *Command executed successfully* otherwise. Then the thread is terminated. """ # Try to connect to an incoming tcp socket using its socket descriptor tcpSocket = QTcpSocket() if not tcpSocket.setSocketDescriptor(self.socketDescriptor): FreeCAD.Console.PrintError("Socket not accepted.\n") return FreeCAD.Console.PrintLog("Socket accepted.\n") # Wait for an incoming message if not tcpSocket.waitForReadyRead(msecs=WAIT_TIME_MS): FreeCAD.Console.PrintError("No request send.\n") return # Make an input data stream instr = QDataStream(tcpSocket) instr.setVersion(QDataStream.Qt_4_0) # Try to read the message size if self.blockSize == 0: if tcpSocket.bytesAvailable() < 2: FreeCAD.Console.PrintError("Received message " + "has too few bytes.\n") return self.blockSize = instr.readUInt16() # Check message is sent complete if tcpSocket.bytesAvailable() < self.blockSize: FreeCAD.Console.PrintError("Received message has less bytes " + "then it's supposed to.\n") return # Read message and inform about it cmd = instr.readRawData(self.blockSize).decode("UTF-8") FreeCAD.Console.PrintLog("CommandServer received> " + cmd + "\n") # Try to execute the message string and prepare a response try: exec(cmd) except Exception as e: FreeCAD.Console.PrintError("Executing external command failed:" + str(e) + "\n") message = "Command failed - " + str(e) else: FreeCAD.Console.PrintLog("Executing external command succeeded!\n") message = COMMAND_EXECUTED_CONFIRMATION_MESSAGE # Prepare the data block to send back and inform about it FreeCAD.Console.PrintLog("CommandServer sending> " + message + " \n") block = QByteArray( len(message.encode("UTF-8")).to_bytes(2, byteorder='big') + message.encode("UTF-8")) outstr = QDataStream(block, QIODevice.WriteOnly) outstr.setVersion(QDataStream.Qt_4_0) # Send the block, disconnect from the socket and terminate the QThread tcpSocket.write(block) tcpSocket.disconnectFromHost() tcpSocket.waitForDisconnected()