Duet3D Logo Duet3D
    • Tags
    • Documentation
    • Order
    • Register
    • Login

    Gcode upload with python and OpenAPI

    Scheduled Pinned Locked Moved Solved
    Third-party software
    3
    14
    614
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • T3P3Tonyundefined
      T3P3Tony administrators @cnc65
      last edited by

      @cnc65 I am not a python expert but I used swagger to generate a python api using 3.6.0.

      Looking at that code for your error message i found it in "rest.py":

        def request(self, method, url, query_params=None, headers=None,
                      body=None, post_params=None, _preload_content=True,
                      _request_timeout=None):
              """Perform requests.
      
              :param method: http request method
              :param url: http request url
              :param query_params: query parameters in the url
              :param headers: http request headers
              :param body: request json body, for `application/json`
              :param post_params: request post parameters,
                                  `application/x-www-form-urlencoded`
                                  and `multipart/form-data`
              :param _preload_content: if False, the urllib3.HTTPResponse object will
                                       be returned without reading/decoding response
                                       data. Default is True.
              :param _request_timeout: timeout setting for this request. If one
                                       number provided, it will be total request
                                       timeout. It can also be a pair (tuple) of
                                       (connection, read) timeouts.
              """
              method = method.upper()
              assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT',
                                'PATCH', 'OPTIONS']
      
              if post_params and body:
                  raise ValueError(
                      "body parameter cannot be used with post_params parameter."
                  )
      
              post_params = post_params or {}
              headers = headers or {}
      
              timeout = None
              if _request_timeout:
                  if isinstance(_request_timeout, (int, ) if six.PY3 else (int, long)):  # noqa: E501,F821
                      timeout = urllib3.Timeout(total=_request_timeout)
                  elif (isinstance(_request_timeout, tuple) and
                        len(_request_timeout) == 2):
                      timeout = urllib3.Timeout(
                          connect=_request_timeout[0], read=_request_timeout[1])
      
              if 'Content-Type' not in headers:
                  headers['Content-Type'] = 'application/json'
      
              try:
                  # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
                  if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
                      if query_params:
                          url += '?' + urlencode(query_params)
                      if re.search('json', headers['Content-Type'], re.IGNORECASE):
                          request_body = '{}'
                          if body is not None:
                              request_body = json.dumps(body)
                          r = self.pool_manager.request(
                              method, url,
                              body=request_body,
                              preload_content=_preload_content,
                              timeout=timeout,
                              headers=headers)
                      elif headers['Content-Type'] == 'application/x-www-form-urlencoded':  # noqa: E501
                          r = self.pool_manager.request(
                              method, url,
                              fields=post_params,
                              encode_multipart=False,
                              preload_content=_preload_content,
                              timeout=timeout,
                              headers=headers)
                      elif headers['Content-Type'] == 'multipart/form-data':
                          # must del headers['Content-Type'], or the correct
                          # Content-Type which generated by urllib3 will be
                          # overwritten.
                          del headers['Content-Type']
                          r = self.pool_manager.request(
                              method, url,
                              fields=post_params,
                              encode_multipart=True,
                              preload_content=_preload_content,
                              timeout=timeout,
                              headers=headers)
                      # Pass a `string` parameter directly in the body to support
                      # other content types than Json when `body` argument is
                      # provided in serialized form
                      elif isinstance(body, str):
                          request_body = body
                          r = self.pool_manager.request(
                              method, url,
                              body=request_body,
                              preload_content=_preload_content,
                              timeout=timeout,
                              headers=headers)
                      else:
                          # Cannot generate the request from given parameters
                          msg = """Cannot prepare a request message for provided
                                   arguments. Please check that your arguments match
                                   declared content type."""
                          raise ApiException(status=0, reason=msg)
                  # For `GET`, `HEAD`
      .....
      

      Does that shed any light on why its failing?

      Things to try:

      Use the CURL commands as a test in the documentation here;
      https://github.com/Duet3D/RepRapFirmware/wiki/HTTP-requests#post-rr_upload
      That proves the duet side of things

      Have thieving on the same version (preferably 3.6, so for RRF use 3.6beta4)

      www.duet3d.com

      1 Reply Last reply Reply Quote 0
      • cnc65undefined
        cnc65
        last edited by

        @T3P3Tony Thank you for your help.
        What I have tried so far:

        1. Update RRF to 3.6beta4
        2. Test CURL commands:
        F:\temp>curl http://192.168.2.1/rr_connect?password=
        {"err":0,"sessionTimeout":8000,"boardType":"duet3mb6hc102","apiLevel":2,"sessionKey":0}
        F:\temp>curl --data-binary @"test.g" "http://192.168.2.1/rr_upload?name=/gcodes/test.g"
        {"err":0}
        F:\temp>
        

        No success with Python so far.

        What I dont understand is that the Python function generated from OpenAPI has only one parameter "name". The parameters "time" and "crc32" are optional. But at least two variables are required: source(path_and_filename.gcode) and destination(name) like in CURL.

        rr_upload_post

        Example

        from __future__ import print_function
        import time
        import swagger_client
        from swagger_client.rest import ApiException
        from pprint import pprint
        
        # create an instance of the API class
        api_instance = swagger_client.DefaultApi()
        name = 'name_example' # str | Path to the file to upload
        time = '2013-10-20T19:20:30+01:00' # datetime | ISO8601-like represenation of the time the file was last modified (optional)
        crc32 = 'crc32_example' # str | CRC32 checksum of the file content as hex string *without* leading `0x`. Usage of this parameter is encouraged (optional)
        
        try:
            # Upload a file 
            api_response = api_instance.rr_upload_post(name, time=time, crc32=crc32)
            pprint(api_response)
        except ApiException as e:
            print("Exception when calling DefaultApi->rr_upload_post: %s\n" % e)
        

        Parameters

        Name Type Description Notes
        name str Path to the file to upload
        time datetime ISO8601-like represenation of the time the file was last modified [optional]
        crc32 str CRC32 checksum of the file content as hex string without leading `0x`. Usage of this parameter is encouraged [optional]
        T3P3Tonyundefined 1 Reply Last reply Reply Quote 0
        • T3P3Tonyundefined
          T3P3Tony administrators @cnc65
          last edited by

          @cnc65 said in Gcode upload with python and OpenAPI:

          so curl is working, there is an issue with the python implementation or the API description in YAML file

          What I don't understand is that the Python function generated from OpenAPI has only one parameter "name". The parameters "time" and "crc32" are optional. But at least two variables are required: source(path_and_filename.gcode) and destination(name) like in CURL.

          I think the "name" parameter is the destination path & filename. however the comments in the YAML file imply its the source:

          parameters:
                         - name: 'name'
                           in: query
                           description: 'Path to the file to upload'
                           required: true
                           schema:
                               type: string
                         - name: 'time'
                           in: query
                           description: 'ISO8601-like represenation of the time the file was last modified'
                           required: false
                           schema:
                               type: string
                               format: 'date-time'
                         - name: 'crc32'
                           in: query
                           description: 'CRC32 checksum of the file content as hex string *without* leading `0x`. Usage of this parameter is encouraged'
                           required: false
                           schema:
                               type: string
          

          @chrishamm can you help?

          www.duet3d.com

          1 Reply Last reply Reply Quote 0
          • cnc65undefined
            cnc65
            last edited by

            Is a solution to this problem in progress?

            chrishammundefined 1 Reply Last reply Reply Quote 0
            • cnc65undefined cnc65 marked this topic as a question
            • chrishammundefined
              chrishamm administrators @cnc65
              last edited by

              @cnc65 @T3P3Tony is right. name specifies the target filename (e.g. /gcodes/foobar.gcode) and the body must contain the file content. I don't know why the generated API does not provide an extra parameter for the file content, though - it's there in the OpenAPI definition.

              Duet software engineer

              T3P3Tonyundefined 1 Reply Last reply Reply Quote 0
              • T3P3Tonyundefined
                T3P3Tony administrators @chrishamm
                last edited by

                @chrishamm can you point to where it is in the API definition? I.e. where is "body" is specified in the definition?

                www.duet3d.com

                chrishammundefined 1 Reply Last reply Reply Quote 0
                • chrishammundefined
                  chrishamm administrators @T3P3Tony
                  last edited by

                  @T3P3Tony Here: https://github.com/Duet3D/RepRapFirmware/blob/3.6-dev/Developer-documentation/OpenAPI.yaml#L174 and here: https://github.com/Duet3D/RepRapFirmware/wiki/HTTP-requests#post-rr_upload

                  Duet software engineer

                  T3P3Tonyundefined 1 Reply Last reply Reply Quote 0
                  • T3P3Tonyundefined
                    T3P3Tony administrators @chrishamm
                    last edited by

                    @chrishamm thanks so this is some issue with swagger?

                    www.duet3d.com

                    chrishammundefined 1 Reply Last reply Reply Quote 0
                    • chrishammundefined
                      chrishamm administrators @T3P3Tony
                      last edited by

                      @T3P3Tony Perhaps it was the GET request, not the POST request? I'm not sure.

                      Duet software engineer

                      T3P3Tonyundefined 1 Reply Last reply Reply Quote 0
                      • T3P3Tonyundefined
                        T3P3Tony administrators @chrishamm
                        last edited by

                        @chrishamm I looked at the swagger post code.

                        www.duet3d.com

                        1 Reply Last reply Reply Quote 0
                        • cnc65undefined
                          cnc65
                          last edited by

                          @chrishamm @T3P3Tony
                          I have changed the requestBody in the OpenAPI.yaml file so that a parameter body is generated in Python. This works for uploading gcode files. I have not tested other files.

                          requestBody:
                          	required: true
                          	description: 'File content'
                          		content:
                          			application/octet-stream:
                          			schema:
                          				type: string
                          				format: binary
                          

                          The function call in Python then looks like this:

                          # Upload a file
                          file = "f:/temp/test.g"
                          name = "0:/gcodes/test-1.g"
                          try:
                              body = open(file).read()
                              duet_response = api_instance.rr_upload_post(body, name)
                          except ApiException as e:
                              print("Exception when calling DefaultApi->rr_upload_post: %s\n" % e)
                          
                          1 Reply Last reply Reply Quote 2
                          • cnc65undefined cnc65 has marked this topic as solved
                          • First post
                            Last post
                          Unless otherwise noted, all forum content is licensed under CC-BY-SA