floopy


Namefloopy JSON
Version 0.1.2 PyPI version JSON
download
home_pageNone
SummaryTester-Agnostic Sequencer for Hardware-Testing in Python
upload_time2024-10-11 18:53:39
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseGPL 3
keywords test sequencer task loop sweep
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <h3 align="center">
    <img width="66%" alt="FLooPy" src="https://raw.githubusercontent.com/floopy-systems/floopy/refs/heads/main/logo-floopy.svg">
    <br><em>Tester-Agnostic Sequencer for Hardware-Testing in Python</em>
    <hr />
</h3>

### Tests
* ... can performed within different **test-environments**
* ... can organized in **hierarchical** layers with (sub-) tasks
* ... can have `setup`, `task/test`, `teardown`, `final` and output functions
* Every variable
    - ... has an unique **namespace**
    - ... is stored as a **time-signal** with timestamps
    - ... can be **sub-classed**
* Data **dependencies** between functions are **resolved** by a graph-based approach
* Auto-Save for loop-recovery and post-processing (experimental)
* Can handle **non-reentrant** tests by an extra extension (unpublished)

### Loops
* Flexible **loop configuration** (nested, zipped, concatenated)
* Standard loops: `loop_items`, `loop_lin`, `loop_log`, `loop_bisect`
* Feedback for **HiL** possible

## Getting Started

Suppose we want to test if the internal resistance of an battery `rbat` is inside the test-limits:

* The test-environment is abstracted by the namespace of the `dut` input and must be specified later, just before running the test-case.
* The loop over the load current `i_load` can be either configured locally inside the test-case or later, just before running the test-case.

### Test-Case


```python
import floopy as fly


class Test_Rbat_Charged(fly.Task):
    dut = fly.Input()  # object to stimulate device-under-test and measure response
   
    i_load = fly.Input(min=0, max=1.5, unit='A', default=fly.loop_lin(num=3))
    
    def task(dut, i_load):
        dut.current = i_load
        return dut.voltage

    def final_fit(i=fly.Squeeze(task.i_load), v=fly.Squeeze(task)):
        import numpy as np
        return np.polyfit(i, v, deg=1)
        
    @fly.Output(min=0, ltl=0.2, utl=2, max=10, unit='ohm')
    def rbat(coeffs=final_fit):
        return coeffs[0]
```

### Test-Plan

Now, the test-case `Test_Rbat_Charged` can be used with different loop-configurations and within different test-environments. For demonstration we just use the simple resistor equation as a simulation-environment.


```python
class TestPlan_BatterCheck(fly.Task):
    def dut():
        class Dut:
            @property
            def voltage(self):
                resistance = 0.8  # ohm
                return resistance * self.current
        return Dut()

    test_rbat_charged = Test_Rbat_Charged(dut, i_load=fly.loop_log(0.1, 1, num=5))
```

Note, that we changed the default loop-configuration of `i_load` to a logarithmic scaling.

In order to perform the Test-Plan we need a `DataManager` saving all loop-states and measurement results as time-signals in a json-file.

```python
>>> dm = fly.DataManager()
>>> dm.run_live(TestPlan_BatterCheck)

╭─ TestPlan_BatterCheck ─╮
│  ✔  test_rbat_charged  │
╰────────────────────────╯
File: dm/dm_2024-10-10_16:00:29.json
```

The test results and all variables can be analyzed via pandas-tables.

```python
>>> dm.read_task(TestPlan_BatterCheck)
```
<div>
<table border="1" class="dataframe">
  <thead>
    <tr>
      <th colspan="2" halign="left">test_rbat_charged</th>
    </tr>
    <tr>
      <th>rbat</th>
      <th>check</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0.8</td>
      <td>True</td>
    </tr>
  </tbody>
</table>
</div>

```python
>>> tp = TestPlan_BatterCheck()
>>> dm.read_task(tp.test_rbat_charged.task)
```
<div>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>i_load</th>
      <th>__return__</th>
    </tr>
    <tr>
      <th>TestPlan_BatterCheck.test_rbat_charged.i_load</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0.100000</th>
      <td>0.100000</td>
      <td>0.080000</td>
    </tr>
    <tr>
      <th>0.177828</th>
      <td>0.177828</td>
      <td>0.142262</td>
    </tr>
    <tr>
      <th>0.316228</th>
      <td>0.316228</td>
      <td>0.252982</td>
    </tr>
    <tr>
      <th>0.562341</th>
      <td>0.562341</td>
      <td>0.449873</td>
    </tr>
    <tr>
      <th>1.000000</th>
      <td>1.000000</td>
      <td>0.800000</td>
    </tr>
  </tbody>
</table>
</div>

For a real measurement we leave the test-case unchanged and just need to replace the `dut` function in the test-plan with something like

```python
def dut():
    import instruments
    dut = instruments.PowerSupply()
    return dut
```

assuming an equal namespace

```python
dut.voltage
dut.current
```

with an equal behavior.

*Further information can be found in the **[FLooPy-Tutorial](./tutorial.ipynb)** which is more detailed.*

## Install


Simply do either

    pip install floopy

or download the repository for a more recent version, change into
the `floopy` folder and

    pip install . --user

If necessary you can run the tests with

    python -m pytest

## Licence

LoopyPlot is licenced under GPL 3.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "floopy",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "test, sequencer, task, loop, sweep",
    "author": null,
    "author_email": "Friedrich Hagedorn <friedrich_h@gmx.de>",
    "download_url": "https://files.pythonhosted.org/packages/4c/04/826501db2f4f37413b0a3ee7fd1d008dd5c3d4613805f3bc79ef182f0407/floopy-0.1.2.tar.gz",
    "platform": null,
    "description": "<h3 align=\"center\">\n    <img width=\"66%\" alt=\"FLooPy\" src=\"https://raw.githubusercontent.com/floopy-systems/floopy/refs/heads/main/logo-floopy.svg\">\n    <br><em>Tester-Agnostic Sequencer for Hardware-Testing in Python</em>\n    <hr />\n</h3>\n\n### Tests\n* ... can performed within different **test-environments**\n* ... can organized in **hierarchical** layers with (sub-) tasks\n* ... can have `setup`, `task/test`, `teardown`, `final` and output functions\n* Every variable\n    - ... has an unique **namespace**\n    - ... is stored as a **time-signal** with timestamps\n    - ... can be **sub-classed**\n* Data **dependencies** between functions are **resolved** by a graph-based approach\n* Auto-Save for loop-recovery and post-processing (experimental)\n* Can handle **non-reentrant** tests by an extra extension (unpublished)\n\n### Loops\n* Flexible **loop configuration** (nested, zipped, concatenated)\n* Standard loops: `loop_items`, `loop_lin`, `loop_log`, `loop_bisect`\n* Feedback for **HiL** possible\n\n## Getting Started\n\nSuppose we want to test if the internal resistance of an battery `rbat` is inside the test-limits:\n\n* The test-environment is abstracted by the namespace of the `dut` input and must be specified later, just before running the test-case.\n* The loop over the load current `i_load` can be either configured locally inside the test-case or later, just before running the test-case.\n\n### Test-Case\n\n\n```python\nimport floopy as fly\n\n\nclass Test_Rbat_Charged(fly.Task):\n    dut = fly.Input()  # object to stimulate device-under-test and measure response\n   \n    i_load = fly.Input(min=0, max=1.5, unit='A', default=fly.loop_lin(num=3))\n    \n    def task(dut, i_load):\n        dut.current = i_load\n        return dut.voltage\n\n    def final_fit(i=fly.Squeeze(task.i_load), v=fly.Squeeze(task)):\n        import numpy as np\n        return np.polyfit(i, v, deg=1)\n        \n    @fly.Output(min=0, ltl=0.2, utl=2, max=10, unit='ohm')\n    def rbat(coeffs=final_fit):\n        return coeffs[0]\n```\n\n### Test-Plan\n\nNow, the test-case `Test_Rbat_Charged` can be used with different loop-configurations and within different test-environments. For demonstration we just use the simple resistor equation as a simulation-environment.\n\n\n```python\nclass TestPlan_BatterCheck(fly.Task):\n    def dut():\n        class Dut:\n            @property\n            def voltage(self):\n                resistance = 0.8  # ohm\n                return resistance * self.current\n        return Dut()\n\n    test_rbat_charged = Test_Rbat_Charged(dut, i_load=fly.loop_log(0.1, 1, num=5))\n```\n\nNote, that we changed the default loop-configuration of `i_load` to a logarithmic scaling.\n\nIn order to perform the Test-Plan we need a `DataManager` saving all loop-states and measurement results as time-signals in a json-file.\n\n```python\n>>> dm = fly.DataManager()\n>>> dm.run_live(TestPlan_BatterCheck)\n\n\u256d\u2500 TestPlan_BatterCheck \u2500\u256e\n\u2502  \u2714  test_rbat_charged  \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\nFile: dm/dm_2024-10-10_16:00:29.json\n```\n\nThe test results and all variables can be analyzed via pandas-tables.\n\n```python\n>>> dm.read_task(TestPlan_BatterCheck)\n```\n<div>\n<table border=\"1\" class=\"dataframe\">\n  <thead>\n    <tr>\n      <th colspan=\"2\" halign=\"left\">test_rbat_charged</th>\n    </tr>\n    <tr>\n      <th>rbat</th>\n      <th>check</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>0.8</td>\n      <td>True</td>\n    </tr>\n  </tbody>\n</table>\n</div>\n\n```python\n>>> tp = TestPlan_BatterCheck()\n>>> dm.read_task(tp.test_rbat_charged.task)\n```\n<div>\n<table border=\"1\" class=\"dataframe\">\n  <thead>\n    <tr style=\"text-align: right;\">\n      <th></th>\n      <th>i_load</th>\n      <th>__return__</th>\n    </tr>\n    <tr>\n      <th>TestPlan_BatterCheck.test_rbat_charged.i_load</th>\n      <th></th>\n      <th></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>0.100000</th>\n      <td>0.100000</td>\n      <td>0.080000</td>\n    </tr>\n    <tr>\n      <th>0.177828</th>\n      <td>0.177828</td>\n      <td>0.142262</td>\n    </tr>\n    <tr>\n      <th>0.316228</th>\n      <td>0.316228</td>\n      <td>0.252982</td>\n    </tr>\n    <tr>\n      <th>0.562341</th>\n      <td>0.562341</td>\n      <td>0.449873</td>\n    </tr>\n    <tr>\n      <th>1.000000</th>\n      <td>1.000000</td>\n      <td>0.800000</td>\n    </tr>\n  </tbody>\n</table>\n</div>\n\nFor a real measurement we leave the test-case unchanged and just need to replace the `dut` function in the test-plan with something like\n\n```python\ndef dut():\n    import instruments\n    dut = instruments.PowerSupply()\n    return dut\n```\n\nassuming an equal namespace\n\n```python\ndut.voltage\ndut.current\n```\n\nwith an equal behavior.\n\n*Further information can be found in the **[FLooPy-Tutorial](./tutorial.ipynb)** which is more detailed.*\n\n## Install\n\n\nSimply do either\n\n    pip install floopy\n\nor download the repository for a more recent version, change into\nthe `floopy` folder and\n\n    pip install . --user\n\nIf necessary you can run the tests with\n\n    python -m pytest\n\n## Licence\n\nLoopyPlot is licenced under GPL 3.\n",
    "bugtrack_url": null,
    "license": "GPL 3",
    "summary": "Tester-Agnostic Sequencer for Hardware-Testing in Python",
    "version": "0.1.2",
    "project_urls": {
        "Homepage": "https://github.com/floopy-systems/floopy"
    },
    "split_keywords": [
        "test",
        " sequencer",
        " task",
        " loop",
        " sweep"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "aed0d13cd6224b07be4e76aad077ac47ad09839d2b31e97610782187d57f1809",
                "md5": "9b8c21df3c7df58f37069e583c4a5c39",
                "sha256": "ae8e80a9824976538511eacf36f0b0b92c3a1d5c7e6714539298d892fdf976b0"
            },
            "downloads": -1,
            "filename": "floopy-0.1.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "9b8c21df3c7df58f37069e583c4a5c39",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 43243,
            "upload_time": "2024-10-11T18:53:37",
            "upload_time_iso_8601": "2024-10-11T18:53:37.889145Z",
            "url": "https://files.pythonhosted.org/packages/ae/d0/d13cd6224b07be4e76aad077ac47ad09839d2b31e97610782187d57f1809/floopy-0.1.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4c04826501db2f4f37413b0a3ee7fd1d008dd5c3d4613805f3bc79ef182f0407",
                "md5": "3b1ed95837489998effa27786896954f",
                "sha256": "3500cc2ed75f681a44cad752adb57cb0bc5612c8ba7d632677b37e46f03d0fa6"
            },
            "downloads": -1,
            "filename": "floopy-0.1.2.tar.gz",
            "has_sig": false,
            "md5_digest": "3b1ed95837489998effa27786896954f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 60442,
            "upload_time": "2024-10-11T18:53:39",
            "upload_time_iso_8601": "2024-10-11T18:53:39.430950Z",
            "url": "https://files.pythonhosted.org/packages/4c/04/826501db2f4f37413b0a3ee7fd1d008dd5c3d4613805f3bc79ef182f0407/floopy-0.1.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-10-11 18:53:39",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "floopy-systems",
    "github_project": "floopy",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "floopy"
}
        
Elapsed time: 0.36385s