Coverage for d7a/alp/parser.py: 89%

137 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 

20# author: Christophe VG <contact@christophe.vg> 

21# a parser for ALP commands 

22import struct 

23 

24from d7a.alp.command import Command 

25from d7a.alp.forward_action import ForwardAction 

26from d7a.alp.indirect_forward_action import IndirectForwardAction 

27from d7a.alp.interface import InterfaceType 

28from d7a.alp.operands.file_header import FileHeaderOperand 

29from d7a.alp.operands.indirect_interface_operand import IndirectInterfaceOperand 

30from d7a.alp.operands.interface_configuration import InterfaceConfiguration 

31from d7a.alp.operands.interface_status import InterfaceStatusOperand 

32from d7a.alp.operands.length import Length 

33from d7a.alp.operands.lorawan_interface_configuration_abp import LoRaWANInterfaceConfigurationABP 

34from d7a.alp.operands.lorawan_interface_configuration_otaa import LoRaWANInterfaceConfigurationOTAA 

35from d7a.alp.operands.query import QueryOperand 

36from d7a.alp.operations.break_query import BreakQuery 

37from d7a.alp.operations.forward import Forward 

38from d7a.alp.operations.indirect_forward import IndirectForward 

39from d7a.alp.operations.status import InterfaceStatus 

40from d7a.alp.operations.tag_response import TagResponse 

41from d7a.alp.operations.write_operations import WriteFileData 

42from d7a.alp.status_action import StatusAction, StatusActionOperandExtensions 

43from d7a.alp.regular_action import RegularAction 

44from d7a.alp.operations.responses import ReturnFileData, ReturnFileHeader 

45from d7a.alp.operations.requests import ReadFileData 

46from d7a.alp.operands.file import Data, DataRequest 

47from d7a.alp.operands.offset import Offset 

48from d7a.alp.tag_response_action import TagResponseAction 

49from d7a.parse_error import ParseError 

50from d7a.sp.configuration import Configuration 

51from d7a.sp.status import Status 

52from d7a.d7anp.addressee import Addressee 

53from d7a.types.ct import CT 

54from d7a.alp.operands.tag_id import TagId 

55from d7a.alp.operations.tag_request import TagRequest 

56from d7a.alp.tag_request_action import TagRequestAction 

57from d7a.phy.channel_header import ChannelHeader 

58from d7a.alp.operations.file_management import CreateNewFile 

59 

60 

61class Parser(object): 

62 def __init__(self, custom_files_class = None): 

63 self.custom_files_class = custom_files_class 

64 

65 def parse(self, s, cmd_length): 

66 actions = [] 

67 if cmd_length != 0: 

68 alp_bytes_parsed = 0 

69 while alp_bytes_parsed < cmd_length: 

70 startpos = s.bytepos 

71 action = self.parse_alp_action(s) 

72 actions.append(action) 

73 alp_bytes_parsed = alp_bytes_parsed + (s.bytepos - startpos) 

74 

75 cmd = Command(actions = actions, generate_tag_request_action=False) 

76 return cmd 

77 

78 def parse_alp_action(self, s): 

79 # meaning of first 2 bits depend on action opcode 

80 b7 = s.read("bool") 

81 b6 = s.read("bool") 

82 op = s.read("uint:6") 

83 try: 

84 return{ 

85 1 : self.parse_alp_read_file_data_action, 

86 4 : self.parse_alp_write_file_data_action, 

87 9 : self.parse_break_query_action, 

88 17 : self.parse_alp_create_file_action, 

89 32 : self.parse_alp_return_file_data_action, 

90 33 : self.parse_alp_return_file_header_action, 

91 34 : self.parse_alp_return_status_action, 

92 35 : self.parse_tag_response_action, 

93 50 : self.parse_forward_action, 

94 51 : self.parse_indirect_forward_action, 

95 52 : self.parse_tag_request_action 

96 }[op](b7, b6, s) 

97 except KeyError: 

98 raise ParseError("alp_action " + str(op) + " is not implemented") 

99 

100 def parse_alp_read_file_data_action(self, b7, b6, s): 

101 operand = self.parse_alp_file_data_request_operand(s) 

102 return RegularAction(group=b7, 

103 resp=b6, 

104 operation=ReadFileData(operand=operand)) 

105 

106 def parse_alp_write_file_data_action(self, b7, b6, s): 

107 operand = self.parse_alp_return_file_data_operand(s) 

108 return RegularAction(group=b7, 

109 resp=b6, 

110 operation=WriteFileData(operand=operand)) 

111 

112 def parse_alp_file_data_request_operand(self, s): 

113 offset = self.parse_offset(s) 

114 length = Length.parse(s) 

115 return DataRequest(length=length, offset=offset) 

116 

117 def parse_break_query_action(self, b7, b6, s): 

118 return RegularAction(group=b7, resp=b6, operation=BreakQuery(operand=QueryOperand.parse(s))) 

119 

120 def parse_alp_create_file_action(self, b7, b6, s): 

121 operand = FileHeaderOperand.parse(s) 

122 return RegularAction(group=b7, 

123 resp=b6, 

124 operation=CreateNewFile(operand=operand)) 

125 

126 def parse_alp_return_file_data_action(self, b7, b6, s): 

127 operand = self.parse_alp_return_file_data_operand(s) 

128 return RegularAction(group=b7, 

129 resp=b6, 

130 operation=ReturnFileData(custom_files_class=self.custom_files_class, operand=operand)) 

131 

132 def parse_alp_return_file_header_action(self, b7, b6, s): 

133 operand = FileHeaderOperand.parse(s) 

134 return RegularAction(group=b7, 

135 resp=b6, 

136 operation=ReturnFileHeader(operand=operand)) 

137 

138 def parse_alp_return_file_data_operand(self, s): 

139 offset = self.parse_offset(s) 

140 length = Length.parse(s) 

141 data = s.read("bytes:" + str(length.value)) 

142 return Data(offset=offset, data=[int(d) for d in data]) 

143 

144 def parse_alp_return_status_action(self, b7, b6, s): 

145 if b7: 

146 raise ParseError("Status Operand extension 2 and 3 is RFU") 

147 

148 if b6: # interface status 

149 interface_id = s.read("uint:8") 

150 length = s.read("uint:8") 

151 try: 

152 interface_status_operation = { 

153 0x00: self.parse_alp_interface_status_host, 

154 0xD7: self.parse_alp_interface_status_d7asp, 

155 0x01: self.parse_alp_interface_status_serial 

156 }[interface_id](s, length) 

157 return StatusAction(operation=interface_status_operation, 

158 status_operand_extension=StatusActionOperandExtensions.INTERFACE_STATUS) 

159 except KeyError: 

160 raise ParseError("Received ALP Interface status for interface " + str(interface_id) + " which is not implemented") 

161 else: # action status 

162 pass # TODO 

163 

164 def parse_tag_request_action(self, b7, b6, s): 

165 if b6: 

166 raise ParseError("bit 6 is RFU") 

167 

168 tag_id = s.read("uint:8") 

169 return TagRequestAction(respond_when_completed=b7, operation=TagRequest(operand=TagId(tag_id=tag_id))) 

170 

171 def parse_tag_response_action(self, b7, b6, s): 

172 tag_id = s.read("uint:8") 

173 return TagResponseAction(eop=b7, error=b6, operation=TagResponse(operand=TagId(tag_id=tag_id))) 

174 

175 def parse_indirect_forward_action(self, b7, b6, s): 

176 interface_file_id = int(s.read("uint:8")) 

177 overload = b7 

178 overload_config = None 

179 if overload: 

180 # TODO we are assuming D7ASP interface here 

181 overload_config = Addressee.parse(s) 

182 

183 return IndirectForwardAction(overload=overload, resp=b6, operation=IndirectForward( 

184 operand=IndirectInterfaceOperand(interface_file_id=interface_file_id, interface_configuration_overload=overload_config))) 

185 

186 def parse_forward_action(self, b7, b6, s): 

187 if b7: 

188 raise ParseError("bit 7 is RFU") 

189 

190 interface_id = InterfaceType(int(s.read("uint:8"))) 

191 interface_config = None 

192 if(interface_id == InterfaceType.D7ASP): 

193 interface_config = Configuration.parse(s) 

194 elif(interface_id == InterfaceType.SERIAL): 

195 pass # no interface config 

196 elif(interface_id == InterfaceType.LORAWAN_ABP): 

197 interface_config = LoRaWANInterfaceConfigurationABP.parse(s) 

198 elif (interface_id == InterfaceType.LORAWAN_OTAA): 

199 interface_config = LoRaWANInterfaceConfigurationOTAA.parse(s) 

200 else: 

201 assert(False) 

202 

203 return ForwardAction(resp=b6, operation=Forward(operand=InterfaceConfiguration(interface_id=interface_id, 

204 interface_configuration=interface_config))) 

205 def parse_alp_interface_status_host(self, s, length): 

206 pass # no interface status defined for host interface 

207 

208 def parse_alp_interface_status_d7asp(self, s, length): 

209 status = None 

210 if length > 0: 

211 status = Status.parse(s) 

212 

213 return InterfaceStatus( 

214 operand=InterfaceStatusOperand(interface_id=0xd7, interface_status=status) 

215 ) 

216 

217 def parse_alp_interface_status_serial(self, s, length): 

218 return InterfaceStatus(operand=InterfaceStatusOperand(interface_id=0x01, interface_status=None)) 

219 

220 def parse_offset(self, s): 

221 return Offset.parse(s)