Coverage for d7a/d7atp/frame.py: 67%

70 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-05-24 08:03 +0200

1# 

2# Copyright (c) 2015-2021 University of Antwerp, Aloxy NV. 

3# 

4# This file is part of pyd7a. 

5# See https://github.com/Sub-IoT/pyd7a for further info. 

6# 

7# Licensed under the Apache License, Version 2.0 (the "License"); 

8# you may not use this file except in compliance with the License. 

9# You may obtain a copy of the License at 

10# 

11# http://www.apache.org/licenses/LICENSE-2.0 

12# 

13# Unless required by applicable law or agreed to in writing, software 

14# distributed under the License is distributed on an "AS IS" BASIS, 

15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

16# See the License for the specific language governing permissions and 

17# limitations under the License. 

18# 

19 

20from d7a.support.schema import Validatable, Types 

21from d7a.d7atp.control import Control 

22from d7a.types.ct import CT 

23from d7a.alp.command import Command 

24from d7a.alp.parser import Parser as AlpParser 

25 

26class Frame(Validatable): 

27 

28 SCHEMA = [{ 

29 "control": Types.OBJECT(Control), 

30 "dialog_id": Types.INTEGER(min=0, max=255), 

31 "transaction_id": Types.INTEGER(min=0, max=255), 

32 "agc_rx_level_i": Types.INTEGER(min=0, max=31), 

33 "tl": Types.OBJECT(CT, nullable=True), 

34 "te": Types.OBJECT(CT, nullable=True), 

35 "tc": Types.OBJECT(CT, nullable=True), 

36 "ack_template": Types.OBJECT(nullable=True), # TODO 

37 "alp_command": Types.OBJECT(Command) 

38 }] 

39 

40 def __init__(self, control, dialog_id, transaction_id, alp_command, agc_rx_level_i=10, tl=None, te=None, tc=None, ack_template=None): 

41 if agc_rx_level_i == None: 

42 agc_rx_level_i = 10 

43 

44 self.control = control 

45 self.dialog_id = dialog_id 

46 self.transaction_id = transaction_id 

47 self.agc_rx_level_i = agc_rx_level_i 

48 self.tl = tl 

49 self.te = te 

50 self.tc = tc 

51 self.ack_template = ack_template 

52 self.alp_command = alp_command 

53 super(Frame, self).__init__() 

54 

55 @staticmethod 

56 def parse(bitstream, payload_length): 

57 control = Control.parse(bitstream) 

58 payload_length = payload_length - 1 # subtract control byte 

59 

60 dialog_id = bitstream.read("uint:8") 

61 payload_length = payload_length - 1 

62 

63 transaction_id = bitstream.read("uint:8") 

64 payload_length = payload_length - 1 

65 

66 target_rx_level_i = None 

67 if control.has_agc: 

68 target_rx_level_i = bitstream.read("uint:8") 

69 payload_length -= 1 

70 

71 tl = None 

72 if control.has_tl: 

73 tl = CT.parse(bitstream) 

74 payload_length -= 1 

75 

76 te = None 

77 if control.has_te: 

78 te = CT.parse(bitstream) 

79 payload_length -= 1 

80 

81 tc = None 

82 # TODO currently we have no way to know if Tc is present or not 

83 # Tc is present when control.is_ack_requested AND when we are requester, 

84 # while responders copy this flag but do NOT provide a Tc. 

85 # When parsing single frames without knowledge of dialogs we cannot determine this. 

86 # We use control.is_dialog_start for now but this will break when we start supporting multiple transactions per dialog 

87 if control.is_ack_requested and control.is_dialog_start: 

88 tc = CT.parse(bitstream) 

89 payload_length -= 1 

90 

91 ack_template = None 

92 if control.is_ack_not_void: 

93 transaction_id_start = bitstream.read("uint:8") 

94 payload_length = payload_length - 1 

95 transaction_id_stop = bitstream.read("uint:8") 

96 payload_length = payload_length - 1 

97 assert transaction_id_start == transaction_id, "Other case not implemented yet" 

98 assert transaction_id_stop == transaction_id, "Other case not implemented yet" 

99 # TODO ack bitmap (for when transaction_id_start != transaction_id) 

100 ack_template = [ transaction_id_start, transaction_id_stop ] 

101 

102 assert control.is_ack_record_requested == False, "Not implemented yet" 

103 assert control.is_ack_not_void == False, "Not implemented yet" 

104 

105 alp_command = AlpParser().parse(bitstream, payload_length) 

106 

107 return Frame( 

108 control=control, 

109 dialog_id=dialog_id, 

110 transaction_id=transaction_id, 

111 agc_rx_level_i=target_rx_level_i, 

112 tl=tl, 

113 te=te, 

114 tc=tc, 

115 ack_template=ack_template, 

116 alp_command=alp_command 

117 ) 

118 

119 def __iter__(self): 

120 for byte in self.control: yield byte 

121 yield self.dialog_id 

122 yield self.transaction_id 

123 if self.control.has_agc: 

124 yield self.agc_rx_level_i 

125 

126 if self.control.has_tl: 

127 yield self.tl 

128 

129 if self.control.has_te: 

130 yield self.te 

131 

132 if self.control.is_ack_not_void: 

133 for byte in self.ack_template: yield byte 

134 

135 for byte in self.alp_command: yield byte