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
« 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#
20# author: Christophe VG <contact@christophe.vg>
21# a parser for ALP commands
22import struct
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
61class Parser(object):
62 def __init__(self, custom_files_class = None):
63 self.custom_files_class = custom_files_class
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)
75 cmd = Command(actions = actions, generate_tag_request_action=False)
76 return cmd
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")
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))
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))
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)
117 def parse_break_query_action(self, b7, b6, s):
118 return RegularAction(group=b7, resp=b6, operation=BreakQuery(operand=QueryOperand.parse(s)))
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))
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))
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))
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])
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")
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
164 def parse_tag_request_action(self, b7, b6, s):
165 if b6:
166 raise ParseError("bit 6 is RFU")
168 tag_id = s.read("uint:8")
169 return TagRequestAction(respond_when_completed=b7, operation=TagRequest(operand=TagId(tag_id=tag_id)))
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)))
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)
183 return IndirectForwardAction(overload=overload, resp=b6, operation=IndirectForward(
184 operand=IndirectInterfaceOperand(interface_file_id=interface_file_id, interface_configuration_overload=overload_config)))
186 def parse_forward_action(self, b7, b6, s):
187 if b7:
188 raise ParseError("bit 7 is RFU")
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)
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
208 def parse_alp_interface_status_d7asp(self, s, length):
209 status = None
210 if length > 0:
211 status = Status.parse(s)
213 return InterfaceStatus(
214 operand=InterfaceStatusOperand(interface_id=0xd7, interface_status=status)
215 )
217 def parse_alp_interface_status_serial(self, s, length):
218 return InterfaceStatus(operand=InterfaceStatusOperand(interface_id=0x01, interface_status=None))
220 def parse_offset(self, s):
221 return Offset.parse(s)