Skip to main content

A python client to interact with the Sedaro Satellite API.

Project description

Sedaro Python Client

A python client for interacting with the Sedaro Satellite API using intuitive classes and methods.

This client is intended to be used alongside our redocs OpenAPI Specification. Please refer to this documentation for detailed information on the names, attributes, and relationships of each Sedaro Block.

Package release versions correspond to the Sedaro Satellite application version at the time of package updates.

Install

pip install sedaro

Use: Block CRUD

  1. Instantiate the SedaroApiClient as a context manager. All code interacting with the API should be within the scope of that context manager. Generate an API key in the Sedaro Satellite Management Console.

    from sedaro import SedaroApiClient
    
    API_KEY = 'my_api_key' # Generated in Sedaro Satellite Management Console
    AGENT_TEMPLATE_BRANCH_ID = 'NShL_CIU9iuufSII49xm-' # id of a Branch owned by my Sedaro account with the given api key
    
    with SedaroApiClient(api_key=API_KEY) as sedaro:
        ...
    
    # If using a dedicated enterprise Sedaro instance, overwrite the default `host` kwarg.
    HOST = 'url-to-my-sedaro-instance.com'
    
    with SedaroApiClient(api_key=API_KEY, host=HOST) as sedaro:
        ...
    
  2. Use the client to instantiate a BranchClient.

    with SedaroApiClient(api_key=API_KEY) as sedaro:
        branch = sedaro.get_branch(AGENT_TEMPLATE_BRANCH_ID)
    
  3. Use the BranchClient to access and utilize BlockClassClients. A BlockClassClient is used to create and access Sedaro Blocks of the respective class.

    branch.BatteryCell
    branch.Component
    branch.Subsystem
    
    # ...etc.
    

    Valid BlockClassClients for Agent Template Branches and Scenario Branches can be found in our redocs OpenAPI Specification, by viewing the valid classes in the blocks key for the Template PATCH route.

    In code editors that support it, intellisense will suggest names for BlockClassClients. Pay attention to what is valid for an Agent Template vs a Scenario branch. If you key into an invalid value, an error will be raised.

  4. A BlockClassClient has several methods:

    branch.Subsystem.create(name='Structure')
    branch.Subsystem.get(block_id) # ID of desired Subsystem
    branch.Subsystem.get_all_ids()
    branch.Subsystem.get_all()
    branch.Subsystem.get_where()
    branch.Subsystem.get_first()
    branch.Subsystem.get_last()
    
  5. These methods (except for get_all_ids) return a single or list of BlockClient(s). A BlockClassClient has several methods and properties.

    subsystem = branch.Subsystem.create(name='Structure')
    
    subsystem.update(name='Structure 2.0')
    
    subsystem.delete()
    

    A BlockClient will always be equal to and in sync with all other BlockClients referencing the same Sedaro Block:

    subsystem = branch.Subsystem.create(name='Structure')
    subsystem_2 = subsystem.update(name='Structure 2.0')
    subsystem_3 = branch.Subsystem.get(subsystem.id)
    
    assert subsystem == subsystem_2 == subsystem_3
    

    The repr of a BlockClient will show you the corresponding Sedaro Block's data:

    repr(subsystem)
    
    >>> Subsystem(
    >>>   category='CUSTOM'
    >>>   components=[]
    >>>   id='NShHxZwUh1JGRfZKDvqdA'
    >>>   name='Structure 2.0'
    >>>   type='Subsystem'
    >>> )
    

    Keying into any field existing on the corresponding Sedaro Block will return the value.

    subsystem.name
    
    >>> 'Structure 2.0'
    

    Keying into relationship fields returns BlockClients corresponding to the related Sedaro Blocks as follows:

    • OneSide: a BlockClient
    • ManySide: a list of BlockClients
    • DataSide: a dictionary with BlockClients as keys and relationship data as values
    solar_panel = subsystem.components[0]
    
    >>> SolarPanel(id='NShKPImRZHxGAXqkPsluk')
    

    Note that this allows for traversing via chained relationship fields.

    solar_panel.cell.panels[-1].subsystem.components[0].delete()
    

Full Example

from sedaro import SedaroApiClient
from sedaro.exceptions import NonexistantBlockError

API_KEY = 'api_key_generated_by_sedaro'
AGENT_TEMPLATE_BRANCH_ID = 'NShL_CIU9iuufSII49xm-'

with SedaroApiClient(api_key=API_KEY) as sedaro:
    branch = sedaro.get_branch(AGENT_TEMPLATE_BRANCH_ID)

    solar_cell = branch.SolarCell.create(
      partNumber="987654321",
      manufacturer='Sedaro Corporation',
      openCircuitVoltage=3.95,
      shortCircuitCurrent=0.36,
      maxPowerVoltage=3.54,
      maxPowerCurrent=0.345,
      numJunctions=3,
    )

    bc_id = solar_cell.id

    solar_cell.update(partNumber="123456789")

    solar_cell.delete()

    try:
        solar_cell.update(partNumber="987654321")
    except NonexistantBlockError as e:
        assert str(e) == f'The referenced "BatteryCell" (id: {bc_id}) no longer exists.'

Multi-Block CRUD

The crud method is also available for performing CRUD operations on multiple Sedaro blocks and/or root at the same time using kwargs as follows:

  • root: update fields on the root by passing a dictionary
  • blocks: create/update 1+ blocks by passing a list of dictionaries. If an id is present, the corresponding block will be updated. If an id isn't present, a new block will be created. The type is always required.
  • delete: delete 1+ blocks by passing a list of their block ids.
with SedaroApiClient(api_key=API_KEY) as sedaro:
    branch = sedaro.get_branch(AGENT_TEMPLATE_BRANCH_ID)

    branch.crud(
        root={ "field": "value" }, # update fields on root
        blocks=[
            { "id": "NTF7...", "type": "Modem", "field": "value" }, # update block
            { "type": "SolarCell",  "field": "value", ... }, # create block
        ],
        delete=["NTF8-90Sh93mPKxJkq6z-"] # delete block
    )

The response from this method is used to update the blocks in the BranchClient the method was called on. The content of the response is also returned, as follows:

{
    "crud": {
      "blocks": [], # ids of all Blocks created or updated
      "delete": [], # ids of all Blocks deleted
  },
    "branch": {
      # whole branch dictionary
  }
}

Use: Simulation

SCENARIO_BRANCH_ID = 'NShL7J0Rni63llTcEUp4F'

with SedaroApiClient(api_key=API_KEY) as sedaro:

    # Instantiate sim client
    sim = sedaro.get_sim_client(SCENARIO_BRANCH_ID)

    # Start simulation
    sim.start()

    # Get simulation
    job_res = sim.get_latest()[0]

    # Check status & percentage complete
    assert job_res['status'] == 'RUNNING'
    print(job_res['progress']['percentComplete'])

    # Terminate simulation
    response = sim.terminate(job_res['id'])
    assert response['message'] == 'Successfully terminated simulation.'

Use: View Results

The primary entrypoint of the results API is the SedaroSimulationResult class. This class offers a few methods for pulling data from scenarios. The most commonly-used method is .get_scenario_latest that pulls the latest results into a new result object. If the simulation is not complete, the resulting object will indicate the status is "Running" and not contain any results.

results = SedaroSimulationResult.get_scenario_latest(api_key, scenario_branch_id)

Alternatively, use the .poll_scenario_latest method to wait for an in-progress simulation to complete and download results after.

results = SedaroSimulationResult.poll_scenario_latest(api_key, scenario_branch_id)

You can also use an optional kwarg, streams, with either of the above functions, to fetch results only from specific streams that you specify. If no argument is provided for streams, all data will be fetched. If you pass an argument to streams, it must be a list of tuples following particular rules:

  • Each tuple in the list can contain either 1 or 2 items.
  • If a tuple contains 1 item, that item must be the agent ID, as a string. Data for all engines of this agent will be fetched. Remember that a 1-item tuple is written like (foo,), NOT like (foo).
  • If a tuple contains 2 items, the first item must be the same as above. The second item must be one of the following strings, specifying an engine: 'GNC, 'CDH', 'Thermal', 'Power'. Data for the specified agent of this engine will be fetched.

For example, with the following code, results will only contain data for all engines of agent foo and the Power and Thermal engines of agent bar.

selected_streams=[
    ('foo',),
    ('bar', 'Thermal'),
    ('bar', 'Power')
]
results = SedaroSimulationResult.get_scenario_latest(api_key, scenario_branch_id, streams=selected_streams)

Any object in the results API will provide a descriptive summary of its contents when the .summarize method is called. See the results_api_demo notebook in the modsim notebooks repository for more examples.

Use: Fetch Raw Data

As an alternative to calling the functions in the SedaroSimulationResult class, you also fetch raw data directly from the SedaroApiClient class, with extra options not available when calling those functions.

with SedaroApiClient(api_key=API_KEY) as sedaro:

    # Instantiate sim client
    sim = sedaro.get_sim_client(SCENARIO_BRANCH_ID)

    # Start simulation
    sim.start()

    # Get simulation
    job_res = sim.get_latest()[0]
    
    # Get raw data
    selected_streams=[
        ('foo',),
        ('bar', 'Thermal'),
        ('bar', 'Power')
    ]
    data = sedaro.get_data(job_res['dataArray'], start=65000, stop=65001, limit=250, streams=selected_streams, axisOrder='TIME_MINOR')
    ### alternative:
    data = sedaro.get_data(job_res['dataArray'], start=65000, stop=65001, binWidth=0.004, streams=selected_streams, axisOrder='TIME_MINOR')

All arguments except the first are optional.

Optional arguments:

  • start (float): the start time of the data to fetch, in MJD format. Defaults to the start of the simulation.
  • stop (float): the end time of the data to fetch, in MJD format. Defaults to the end of the simulation.
  • limit (int): the maximum number of points in time for which to fetch data for any stream. If not specified, there is no limit and data is fetched at full resolution. If a limit is specified, the duration of the time from start to stop is divided into the specified number of bins of equal duration, and data is selected from at most one point in time within each bin. Not that it is not guaranteed that you will receive exactly as many points in time as the limit you specify; you may receive fewer, depending on the length of a data stream and/or the distribution of data point timestamps through the simulation.
  • binWidth (float): the width of the bins used in downsampling data, as described for limit. Note that binWidth and limit are not meant to be used together; undefined behavior may occur. If you would like to downsample data, use either limit or binWidth, but not both.
  • streams (list): specify which data streams you would like to fetch data for, according to the format described in the previous section. If no list is provided, data is fetched for all streams.
  • axisOrder (enum): the shape of each series in the response. Options: 'TIME_MAJOR' and 'TIME_MINOR'. Default value, if not specified, is 'TIME_MAJOR'.

Use: Send Requests

Use built-in method to send customized requests to the host. See OpenAPI Specification for documentation on resource paths and body params.

with SedaroApiClient(api_key=API_KEY) as sedaro:
    # get a branch
    sedaro.send_request(
        f'/models/branches/{AGENT_TEMPLATE_BRANCH_ID}',
        'GET'
    )

    # create a celestial target in a branch
    sun = {
        'name': 'Sun',
        'type': 'CelestialTarget'
    }

    sedaro.send_request(
        f'/models/branches/{self.id}/template/',
        'PATCH',
        { 'blocks': [sun] }
    )

Note that requests sent this way to CRUD Sedaro Blocks won't automatically update already instantiated BranchClients or BlockClients.

Further information

See docstrings on classes and their methods for further instructions and explanations.

Sedaro Base Client

The Sedaro client is a wrapper around the Swagger generated OpenAPI client. When this package is installed, the auto-generated, lower-level clients and methods are also available under sedaro_base_client.

from sedaro_base_client import ...

Community, Support, Discussion

If you have any issues using the package or any suggestions, please start by reaching out:

  1. Open an issue on GitHub
  2. Join the Sedaro Community Slack
  3. Email us at support@sedarotech.com

Please note that while emails are always welcome, we prefer the first two options as this allows for others to benefit from the discourse in the threads. That said, if the matter is specific to your use case or sensitive in nature, don't hesitate to shoot us an email instead.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sedaro-4.1.1.tar.gz (266.7 kB view hashes)

Uploaded Source

Built Distribution

sedaro-4.1.1-py3-none-any.whl (618.8 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page