1#!/usr/bin/env python3
2#
3#  Copyright (c) 2023, The OpenThread Authors.
4#  All rights reserved.
5#
6#  Redistribution and use in source and binary forms, with or without
7#  modification, are permitted provided that the following conditions are met:
8#  1. Redistributions of source code must retain the above copyright
9#     notice, this list of conditions and the following disclaimer.
10#  2. Redistributions in binary form must reproduce the above copyright
11#     notice, this list of conditions and the following disclaimer in the
12#     documentation and/or other materials provided with the distribution.
13#  3. Neither the name of the copyright holder nor the
14#     names of its contributors may be used to endorse or promote products
15#     derived from this software without specific prior written permission.
16#
17#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
18#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27#  POSSIBILITY OF SUCH DAMAGE.
28#
29
30import unittest
31
32import config
33import mle
34import thread_cert
35from pktverify import consts
36
37LEADER = 1
38ED = 2
39SSED = 3
40
41
42class SSED_CslChannelManager(thread_cert.TestCase):
43    TOPOLOGY = {
44        LEADER: {
45            'version': '1.2',
46        },
47        ED: {
48            'version': '1.2',
49            'is_mtd': False,
50            'mode': 'rn',
51        },
52        SSED: {
53            'version': '1.2',
54            'is_mtd': True,
55            'mode': '-',
56        },
57    }
58    """All nodes are created with default configurations"""
59
60    def test(self):
61
62        self.nodes[SSED].set_csl_period(consts.CSL_DEFAULT_PERIOD)
63        self.nodes[SSED].set_csl_timeout(consts.CSL_DEFAULT_TIMEOUT)
64
65        self.nodes[SSED].get_csl_info()
66
67        self.nodes[LEADER].start()
68        self.simulator.go(config.LEADER_STARTUP_DELAY)
69        self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
70        channel = self.nodes[LEADER].get_channel()
71
72        self.nodes[SSED].start()
73        self.simulator.go(7)
74        self.assertEqual(self.nodes[SSED].get_state(), 'child')
75
76        csl_channel = 0
77        csl_config = self.nodes[SSED].get_csl_info()
78        self.assertTrue(int(csl_config['channel']) == csl_channel)
79        self.assertTrue(csl_config['period'] == '500000us')
80
81        print('SSED rloc:%s' % self.nodes[SSED].get_rloc())
82        self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED].get_rloc()))
83
84        # let channel monitor collect >970 sample counts
85        self.simulator.go(980 * 41)
86        results = self.nodes[SSED].get_channel_monitor_info()
87        self.assertTrue(int(results['count']) > 970)
88
89        # Configure channel manager channel masks
90        # Set cca threshold to 0 as we cannot change cca assessment in simulation.
91        # and shorten interval to speedup test
92        all_channels_mask = int('0x7fff800', 0)
93        chan_12_to_15_mask = int('0x000f000', 0)
94        interval = 30
95        self.nodes[SSED].set_channel_manager_supported(all_channels_mask)
96        self.nodes[SSED].set_channel_manager_favored(chan_12_to_15_mask)
97        self.nodes[SSED].set_channel_manager_cca_threshold('0x0000')
98        self.nodes[SSED].set_channel_manager_interval(interval)
99
100        # enable channel manager auto-select and check
101        # network channel is not changed by channel manager on SSED
102        # and also csl_channel is unchanged
103        self.nodes[SSED].set_channel_manager_auto_enable(True)
104        self.simulator.go(interval + 1)
105        results = self.nodes[SSED].get_channel_manager_config()
106        self.assertTrue(int(results['auto']) == 1)
107        self.assertTrue(results['cca threshold'] == '0x0000')
108        self.assertTrue(int(results['interval']) == interval)
109        self.assertTrue('11-26' in results['supported'])
110        self.simulator.go(1)
111        self.assertTrue(self.nodes[SSED].get_channel() == channel)
112        csl_config = self.nodes[SSED].get_csl_info()
113        self.assertTrue(int(csl_config['channel']) == csl_channel)
114
115        # check SSED can change csl channel
116        csl_channel = 25
117        self.flush_all()
118        self.nodes[SSED].set_csl_channel(csl_channel)
119        self.simulator.go(1)
120        ssed_messages = self.simulator.get_messages_sent_by(SSED)
121        self.assertIsNotNone(ssed_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST))
122        self.simulator.go(1)
123        csl_config = self.nodes[SSED].get_csl_info()
124        self.assertTrue(int(csl_config['channel']) == csl_channel)
125        self.simulator.go(1)
126        self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED].get_rloc()))
127
128        # enable channel manager autocsl-select in addition
129        # and check csl channel changed to best favored channel 12
130        csl_channel = 12
131        self.nodes[SSED].set_channel_manager_autocsl_enable(True)
132        self.simulator.go(interval + 1)
133        results = self.nodes[SSED].get_channel_manager_config()
134        self.assertTrue(int(results['autocsl']) == 1)
135        self.assertTrue(int(results['channel']) == csl_channel)
136        csl_config = self.nodes[SSED].get_csl_info()
137        self.assertTrue(int(csl_config['channel']) == csl_channel)
138        self.simulator.go(1)
139        self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED].get_rloc()))
140
141
142if __name__ == '__main__':
143    unittest.main()
144