Skip to content

Commit

Permalink
Add example script that uses the ParamFileHelper.
Browse files Browse the repository at this point in the history
The script will write all supplied parameters to the crazyflie and store them

Re-format code to code standard
  • Loading branch information
ToveRumar committed Jun 17, 2024
1 parent 3db0bcd commit a0df110
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 38 deletions.
10 changes: 6 additions & 4 deletions cflib/utils/param_file_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,23 @@
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from threading import Event

from cflib.crazyflie import Crazyflie
from cflib.localization.param_io import ParamFileManager
from threading import Event


class ParamFileHelper:
'''ParamFileHelper is a helper to synchonously write multiple paramteters
from a file and store them in persistent memory'''

def __init__(self, crazyflie):
if isinstance(crazyflie, Crazyflie):
self._cf = crazyflie
self.persistent_sema = None
self.success = False
else:
raise TypeError("ParamFileHelper only takes a Crazyflie Object")

raise TypeError('ParamFileHelper only takes a Crazyflie Object')

def _persistent_stored_callback(self, complete_name, success):
self.success = success
Expand All @@ -42,7 +45,6 @@ def _persistent_stored_callback(self, complete_name, success):
print(f'Persistent params: stored {complete_name}!')
self.persistent_sema.set()


def store_params_from_file(self, filename):
params = ParamFileManager().read(filename)
for param, state in params.items():
Expand Down
60 changes: 60 additions & 0 deletions examples/parameters/persistent_params_from_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
#
# ,---------, ____ _ __
# | ,-^-, | / __ )(_) /_______________ _____ ___
# | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \
# | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
# +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/
#
# Copyright (C) 2024 Bitcraze AB
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, in version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Example to show how to write several persistent parameters from a yaml file.
The params in the file should be formatted like this;
params:
activeMarker.back:
default_value: 3
is_stored: true
stored_value: 30
type: persistent_param_state
version: '1'
"""
import argparse
import logging

import cflib.crtp
from cflib.crazyflie import Crazyflie
from cflib.crazyflie.syncCrazyflie import SyncCrazyflie
from cflib.utils import uri_helper
from cflib.utils.param_file_helper import ParamFileHelper


uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7')

# Only output errors from the logging framework
logging.basicConfig(level=logging.ERROR)


if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--file', type=str, help='The yaml file containing the arguments. ')
args = parser.parse_args()

cflib.crtp.init_drivers()

cf = Crazyflie(rw_cache='./cache')
with SyncCrazyflie(uri, cf=cf) as scf:
writer = ParamFileHelper(scf.cf)
writer.store_params_from_file(args.file)
69 changes: 35 additions & 34 deletions test/utils/test_param_file_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import unittest
from threading import Event
from unittest.mock import MagicMock
from unittest.mock import patch
from cflib.utils.param_file_helper import ParamFileHelper

from cflib.crazyflie import Crazyflie
from cflib.crazyflie.syncCrazyflie import SyncCrazyflie
from threading import Event
from cflib.utils.param_file_helper import ParamFileHelper


class ParamFileHelperTests(unittest.TestCase):

Expand All @@ -38,82 +40,81 @@ def test_ParamFileHelper_SyncCrazyflieAsParam_ThrowsException(self):
helper = None
try:
helper = ParamFileHelper(cf_mock)
except Exception as e:
self.assertIsNone(helper)
except Exception:
self.assertIsNone(helper)
else:
self.fail('Expect exception')


def test_ParamFileHelper_Crazyflie_Object(self):
helper = ParamFileHelper(self.cf_mock)
self.assertIsNotNone(helper)
self.assertIsNotNone(helper)

@patch('cflib.crazyflie.Param')
def test_ParamFileHelper_writesAndStoresParamFromFileToCrazyflie(self, mock_Param):
#Setup
# Setup
cf_mock = MagicMock(spec=Crazyflie)
cf_mock.param = mock_Param
helper = ParamFileHelper(cf_mock)
#Mock blocking wait and call callback instead. This lets the flow work as it would in the asynch world
def mock_wait(self,timeout=None):
# Mock blocking wait and call callback instead. This lets the flow work as it would in the asynch world

def mock_wait(self, timeout=None):
helper._persistent_stored_callback('activeMarker.back', True)
return

with patch.object(Event, 'wait', new=mock_wait):
self.assertTrue(helper.store_params_from_file('test/utils/fixtures/single_param.yaml'))
mock_Param.set_value.assert_called_once_with('activeMarker.back',10)
mock_Param.set_value.assert_called_once_with('activeMarker.back', 10)
mock_Param.persistent_store.assert_called_once_with('activeMarker.back', helper._persistent_stored_callback)

@patch('cflib.crazyflie.Param')
def test_ParamFileHelper_writesParamAndFailsToSetPersistantShouldReturnFalse(self, mock_Param):
#Setup
# Setup
cf_mock = MagicMock(spec=Crazyflie)
cf_mock.param = mock_Param
helper = ParamFileHelper(cf_mock)
#Mock blocking wait and call callback instead. This lets the flow work as it would in the asynch world
def mock_wait(self,timeout=None):
# Mock blocking wait and call callback instead. This lets the flow work as it would in the asynch world

def mock_wait(self, timeout=None):
helper._persistent_stored_callback('activeMarker.back', False)
return

with patch.object(Event, 'wait', new=mock_wait):
self.assertFalse(helper.store_params_from_file('test/utils/fixtures/single_param.yaml'))
mock_Param.set_value.assert_called_once_with('activeMarker.back',10)
mock_Param.set_value.assert_called_once_with('activeMarker.back', 10)
mock_Param.persistent_store.assert_called_once_with('activeMarker.back', helper._persistent_stored_callback)

@patch('cflib.crazyflie.Param')
def test_ParamFileHelper_TryWriteSeveralParamsPersistantShouldBreakAndReturnFalse(self, mock_Param):
#Setup
# Setup
cf_mock = MagicMock(spec=Crazyflie)
cf_mock.param = mock_Param
helper = ParamFileHelper(cf_mock)
#Mock blocking wait and call callback instead. This lets the flow work as it would in the asynch world
def mock_wait(self,timeout=None):
# Mock blocking wait and call callback instead. This lets the flow work as it would in the asynch world

def mock_wait(self, timeout=None):
helper._persistent_stored_callback('activeMarker.back', False)
return

with patch.object(Event, 'wait', new=mock_wait):
#Test and assert
# Test and assert
self.assertFalse(helper.store_params_from_file('test/utils/fixtures/five_params.yaml'))
#Assert it breaks directly by checking number of calls
mock_Param.set_value.assert_called_once_with('activeMarker.back',10)
# Assert it breaks directly by checking number of calls
mock_Param.set_value.assert_called_once_with('activeMarker.back', 10)
mock_Param.persistent_store.assert_called_once_with('activeMarker.back', helper._persistent_stored_callback)

@patch('cflib.crazyflie.Param')
def test_ParamFileHelper_writesAndStoresAllParamsFromFileToCrazyflie(self, mock_Param):
#Setup
# Setup
cf_mock = MagicMock(spec=Crazyflie)
cf_mock.param = mock_Param
helper = ParamFileHelper(cf_mock)
#Mock blocking wait and call callback instead. This lets the flow work as it would in the asynch world
def mock_wait(self,timeout=None):
# Mock blocking wait and call callback instead. This lets the flow work as it would in the asynch world

def mock_wait(self, timeout=None):
helper._persistent_stored_callback('something', True)
return
with patch.object(Event, 'wait', new=mock_wait):
#Test and Assert
# Test and Assert
self.assertTrue(helper.store_params_from_file('test/utils/fixtures/five_params.yaml'))
self.assertEquals(5,len(mock_Param.set_value.mock_calls))
self.assertEquals(5,len(mock_Param.persistent_store.mock_calls))




self.assertEquals(5, len(mock_Param.set_value.mock_calls))
self.assertEquals(5, len(mock_Param.persistent_store.mock_calls))

0 comments on commit a0df110

Please sign in to comment.