mbed Connector Endpoint interface. This interface permits a mbed endpoint to easily setup MDS resources and emit those resources to an MDS server.

Dependents:   IoT_LED_demo ServoTest uWater_Project hackathon ... more

Committer:
ansond
Date:
Fri Mar 20 04:08:59 2015 +0000
Revision:
24:a6915e19814e
Parent:
23:caa0260acc21
Child:
25:1fc958ac14d1
added DataWrapper

Who changed what in which revision?

UserRevisionLine numberNew contents of line
ansond 0:b438482ebbfc 1 /**
ansond 0:b438482ebbfc 2 * @file DynamicResource.cpp
ansond 0:b438482ebbfc 3 * @brief mbed CoAP Endpoint Dynamic Resource class
ansond 0:b438482ebbfc 4 * @author Doug Anson/Chris Paola
ansond 0:b438482ebbfc 5 * @version 1.0
sam_grove 2:853f9ecc12df 6 * @see
ansond 0:b438482ebbfc 7 *
ansond 0:b438482ebbfc 8 * Copyright (c) 2014
ansond 0:b438482ebbfc 9 *
ansond 0:b438482ebbfc 10 * Licensed under the Apache License, Version 2.0 (the "License");
ansond 0:b438482ebbfc 11 * you may not use this file except in compliance with the License.
ansond 0:b438482ebbfc 12 * You may obtain a copy of the License at
ansond 0:b438482ebbfc 13 *
ansond 0:b438482ebbfc 14 * http://www.apache.org/licenses/LICENSE-2.0
ansond 0:b438482ebbfc 15 *
ansond 0:b438482ebbfc 16 * Unless required by applicable law or agreed to in writing, software
ansond 0:b438482ebbfc 17 * distributed under the License is distributed on an "AS IS" BASIS,
ansond 0:b438482ebbfc 18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
ansond 0:b438482ebbfc 19 * See the License for the specific language governing permissions and
ansond 0:b438482ebbfc 20 * limitations under the License.
ansond 0:b438482ebbfc 21 */
sam_grove 2:853f9ecc12df 22
sam_grove 2:853f9ecc12df 23 #include "DynamicResource.h"
sam_grove 2:853f9ecc12df 24
sam_grove 2:853f9ecc12df 25 // InstancePointerTable Helper
sam_grove 2:853f9ecc12df 26 #include "InstancePointerTableHelper.h"
sam_grove 2:853f9ecc12df 27
sam_grove 2:853f9ecc12df 28 // default constructor
sam_grove 2:853f9ecc12df 29 DynamicResource::DynamicResource(const Logger *logger,const char *name,const char *res_type,uint8_t res_mask,const bool observable) : Resource<string>(logger,string(name),string(""))
sam_grove 2:853f9ecc12df 30 {
ansond 0:b438482ebbfc 31 this->m_res_type = string(res_type);
ansond 0:b438482ebbfc 32 this->m_observable = observable;
sam_grove 2:853f9ecc12df 33 this->m_res_mask = res_mask;
ansond 21:8487990a3baa 34 this->m_obs_number = 0;
ansond 21:8487990a3baa 35 this->m_obs_token_ptr = NULL;
ansond 21:8487990a3baa 36 this->m_obs_token_len = 0;
ansond 24:a6915e19814e 37 this->m_data_wrapper = NULL;
sam_grove 2:853f9ecc12df 38 }
sam_grove 2:853f9ecc12df 39
sam_grove 2:853f9ecc12df 40 // constructor (input initial value)
sam_grove 2:853f9ecc12df 41 DynamicResource::DynamicResource(const Logger *logger,const char *name,const char *res_type,const string value,uint8_t res_mask,const bool observable) : Resource<string>(logger,string(name),value)
sam_grove 2:853f9ecc12df 42 {
ansond 0:b438482ebbfc 43 this->m_res_type = string(res_type);
ansond 0:b438482ebbfc 44 this->m_observable = observable;
sam_grove 2:853f9ecc12df 45 this->m_res_mask = res_mask;
ansond 21:8487990a3baa 46 this->m_obs_number = 0;
ansond 21:8487990a3baa 47 this->m_obs_token_ptr = NULL;
ansond 21:8487990a3baa 48 this->m_obs_token_len = 0;
ansond 24:a6915e19814e 49 this->m_data_wrapper = NULL;
sam_grove 2:853f9ecc12df 50 }
sam_grove 2:853f9ecc12df 51
sam_grove 2:853f9ecc12df 52 // constructor (strings)
sam_grove 2:853f9ecc12df 53 DynamicResource::DynamicResource(const Logger *logger,const string name,const string res_type,const string value,uint8_t res_mask,const bool observable) : Resource<string>(logger,name,value)
sam_grove 2:853f9ecc12df 54 {
ansond 0:b438482ebbfc 55 this->m_res_type = res_type;
ansond 0:b438482ebbfc 56 this->m_observable = observable;
sam_grove 2:853f9ecc12df 57 this->m_res_mask = res_mask;
ansond 21:8487990a3baa 58 this->m_obs_number = 0;
ansond 21:8487990a3baa 59 this->m_obs_token_ptr = NULL;
ansond 21:8487990a3baa 60 this->m_obs_token_len = 0;
ansond 24:a6915e19814e 61 this->m_data_wrapper = NULL;
sam_grove 2:853f9ecc12df 62 }
sam_grove 2:853f9ecc12df 63
sam_grove 2:853f9ecc12df 64 // copy constructor
sam_grove 2:853f9ecc12df 65 DynamicResource::DynamicResource(const DynamicResource &resource) : Resource<string>((const Resource<string> &)resource)
sam_grove 2:853f9ecc12df 66 {
ansond 0:b438482ebbfc 67 this->m_res_type = resource.m_res_type;
ansond 0:b438482ebbfc 68 this->m_observable = resource.m_observable;
ansond 0:b438482ebbfc 69 this->m_res_mask = resource.m_res_mask;
ansond 21:8487990a3baa 70 this->m_obs_number = resource.m_obs_number;
ansond 21:8487990a3baa 71 this->m_obs_token_ptr = resource.m_obs_token_ptr;
ansond 21:8487990a3baa 72 this->m_obs_token_len = resource.m_obs_token_len;
ansond 24:a6915e19814e 73 this->m_data_wrapper = resource.m_data_wrapper;
sam_grove 2:853f9ecc12df 74 }
sam_grove 2:853f9ecc12df 75
sam_grove 2:853f9ecc12df 76 // destructor
sam_grove 2:853f9ecc12df 77 DynamicResource::~DynamicResource()
sam_grove 2:853f9ecc12df 78 {
ansond 21:8487990a3baa 79 if(this->m_obs_token_ptr) free(this->m_obs_token_ptr);
sam_grove 2:853f9ecc12df 80 }
sam_grove 2:853f9ecc12df 81
sam_grove 2:853f9ecc12df 82 // bind resource to NSDL
sam_grove 2:853f9ecc12df 83 void DynamicResource::bind(void *p)
sam_grove 2:853f9ecc12df 84 {
ansond 0:b438482ebbfc 85 if (p != NULL) {
ansond 0:b438482ebbfc 86 sn_nsdl_resource_info_s *resource_ptr = (sn_nsdl_resource_info_s *)p;
ansond 0:b438482ebbfc 87 const uint8_t *name = (const uint8_t *)(this->getName().c_str());
ansond 0:b438482ebbfc 88 const uint8_t *res_type = (const uint8_t *)this->m_res_type.c_str();
ansond 0:b438482ebbfc 89 int name_length = this->getName().size();
ansond 0:b438482ebbfc 90 int res_type_length = this->m_res_type.size();
sam_grove 2:853f9ecc12df 91 int is_observable = 0;
sam_grove 2:853f9ecc12df 92 if (this->m_observable == true) is_observable = 1;
ansond 0:b438482ebbfc 93 const string *key = new string(this->getName());
ansond 0:b438482ebbfc 94 ipt_helper_add_instance_pointer(key,this);
ansond 0:b438482ebbfc 95 nsdl_create_dynamic_resource(resource_ptr,name_length,(uint8_t *)name,res_type_length,(uint8_t *)res_type,is_observable,&ipt_helper_nsdl_callback_stub,this->m_res_mask);
ansond 20:abfbaf524192 96 this->logger()->log("DynamicResource: [%s] type: [%s] bound (observable: %d)",name,res_type,is_observable);
sam_grove 2:853f9ecc12df 97 } else {
ansond 5:a929d65eb385 98 this->logger()->log("DynamicResource: NULL parameter in bind()");
ansond 0:b438482ebbfc 99 }
sam_grove 2:853f9ecc12df 100 }
ansond 0:b438482ebbfc 101
sam_grove 2:853f9ecc12df 102 // process NSDL message
sam_grove 2:853f9ecc12df 103 uint8_t DynamicResource::process(sn_coap_hdr_s *received_coap_ptr, sn_nsdl_addr_s *address, sn_proto_info_s *proto)
sam_grove 2:853f9ecc12df 104 {
ansond 0:b438482ebbfc 105 sn_coap_hdr_s *coap_res_ptr = 0;
sam_grove 2:853f9ecc12df 106
ansond 0:b438482ebbfc 107 // create our key for debugging output...
ansond 0:b438482ebbfc 108 string key = this->coapDataToString(received_coap_ptr->uri_path_ptr,received_coap_ptr->uri_path_len);
sam_grove 2:853f9ecc12df 109
sam_grove 2:853f9ecc12df 110 if(received_coap_ptr->msg_code == COAP_MSG_CODE_REQUEST_GET) {
ansond 0:b438482ebbfc 111 coap_res_ptr = sn_coap_build_response(received_coap_ptr, COAP_MSG_CODE_RESPONSE_CONTENT);
sam_grove 2:853f9ecc12df 112
sam_grove 2:853f9ecc12df 113 // process the GET if we have registered a callback for it...
ansond 0:b438482ebbfc 114 if ((this->m_res_mask&SN_GRS_GET_ALLOWED) != 0) {
ansond 0:b438482ebbfc 115 // call the resource get() to get the resource value
ansond 5:a929d65eb385 116 this->logger()->log("Calling resource(GET) for [%s]...",key.c_str());
ansond 0:b438482ebbfc 117 string value = this->get();
sam_grove 2:853f9ecc12df 118
ansond 24:a6915e19814e 119 // convert the string from the GET to something suitable for CoAP payloads
ansond 24:a6915e19814e 120 if (this->getDataWrapper() != NULL) {
ansond 24:a6915e19814e 121 // wrap the data...
ansond 24:a6915e19814e 122 this->getDataWrapper()->wrap((uint8_t *)value.c_str(),(int)value.size());
ansond 24:a6915e19814e 123
ansond 24:a6915e19814e 124 // announce (after wrap)
ansond 24:a6915e19814e 125 this->logger()->log("Building payload for [%s]=[%s]...",key.c_str(),this->getDataWrapper()->get());
ansond 24:a6915e19814e 126
ansond 24:a6915e19814e 127 // fill in the CoAP response payload
ansond 24:a6915e19814e 128 coap_res_ptr->payload_len = this->getDataWrapper()->length();
ansond 24:a6915e19814e 129 coap_res_ptr->payload_ptr = this->getDataWrapper()->get();
ansond 24:a6915e19814e 130 }
ansond 24:a6915e19814e 131 else {
ansond 24:a6915e19814e 132 // announce (no wrap)
ansond 24:a6915e19814e 133 this->logger()->log("Building payload for [%s]=[%s]...",key.c_str(),value.c_str());
ansond 24:a6915e19814e 134
ansond 24:a6915e19814e 135 // do not wrap the data...
ansond 24:a6915e19814e 136 coap_res_ptr->payload_len = value.size();
ansond 24:a6915e19814e 137 coap_res_ptr->payload_ptr = (uint8_t *)value.c_str();
ansond 24:a6915e19814e 138 }
ansond 21:8487990a3baa 139
ansond 21:8487990a3baa 140 // Observation handling...
ansond 21:8487990a3baa 141 if(received_coap_ptr->token_ptr) {
ansond 21:8487990a3baa 142 if(this->m_obs_token_ptr) {
ansond 21:8487990a3baa 143 free(this->m_obs_token_ptr);
ansond 21:8487990a3baa 144 this->m_obs_token_ptr = NULL;
ansond 23:caa0260acc21 145 this->m_obs_token_len = 0;
ansond 21:8487990a3baa 146 }
ansond 22:192b598ba389 147
ansond 21:8487990a3baa 148 this->m_obs_token_ptr = (uint8_t*)malloc(received_coap_ptr->token_len);
ansond 21:8487990a3baa 149 if(this->m_obs_token_ptr) {
ansond 21:8487990a3baa 150 memcpy(this->m_obs_token_ptr, received_coap_ptr->token_ptr,received_coap_ptr->token_len);
ansond 21:8487990a3baa 151 this->m_obs_token_len = received_coap_ptr->token_len;
ansond 21:8487990a3baa 152 }
ansond 21:8487990a3baa 153 }
ansond 21:8487990a3baa 154
ansond 22:192b598ba389 155 // Observation handling...
ansond 22:192b598ba389 156 if(received_coap_ptr->options_list_ptr && received_coap_ptr->options_list_ptr->observe) {
ansond 21:8487990a3baa 157 coap_res_ptr->options_list_ptr = (sn_coap_options_list_s*)malloc(sizeof(sn_coap_options_list_s));
ansond 21:8487990a3baa 158 memset(coap_res_ptr->options_list_ptr, 0, sizeof(sn_coap_options_list_s));
ansond 22:192b598ba389 159 coap_res_ptr->options_list_ptr->observe_ptr = &this->m_obs_number; // see nullify note below...
ansond 21:8487990a3baa 160 coap_res_ptr->options_list_ptr->observe_len = 1;
ansond 21:8487990a3baa 161 this->m_obs_number++;
ansond 21:8487990a3baa 162 }
sam_grove 2:853f9ecc12df 163
sam_grove 2:853f9ecc12df 164 // build out the response and send...
ansond 21:8487990a3baa 165 sn_nsdl_send_coap_message(address,coap_res_ptr);
ansond 22:192b598ba389 166
ansond 22:192b598ba389 167 //
ansond 22:192b598ba389 168 // nullify note:
ansond 22:192b598ba389 169 //
ansond 22:192b598ba389 170 // because our obs_number (assigned to observe_ptr) is part of this object instance, we dont
ansond 22:192b598ba389 171 // want to have the underlying free() try to free it... to just nullify here
ansond 22:192b598ba389 172 //
ansond 22:192b598ba389 173 if (coap_res_ptr->options_list_ptr) coap_res_ptr->options_list_ptr->observe_ptr = 0;
ansond 21:8487990a3baa 174 }
ansond 21:8487990a3baa 175 else {
ansond 5:a929d65eb385 176 this->logger()->log("ERROR: resource(GET) mask is munged (mask: 0x%x)",this->m_res_mask);
ansond 0:b438482ebbfc 177 }
sam_grove 2:853f9ecc12df 178 } else if(received_coap_ptr->msg_code == COAP_MSG_CODE_REQUEST_PUT) {
sam_grove 2:853f9ecc12df 179 if(received_coap_ptr->payload_len > 0) {
ansond 0:b438482ebbfc 180 // process the PUT if we have registered a callback for it...
ansond 0:b438482ebbfc 181 if ((this->m_res_mask&SN_GRS_PUT_ALLOWED) != 0) {
ansond 0:b438482ebbfc 182 // binder interacts only with strings
ansond 0:b438482ebbfc 183 string value = this->coapDataToString(received_coap_ptr->payload_ptr,received_coap_ptr->payload_len);
sam_grove 2:853f9ecc12df 184
ansond 0:b438482ebbfc 185 // call the resource put() to set the resource value
ansond 5:a929d65eb385 186 this->logger()->log("Calling resource(PUT) with [%s]=[%s]...",key.c_str(),value.c_str());
ansond 0:b438482ebbfc 187 this->put(value);
sam_grove 2:853f9ecc12df 188
ansond 0:b438482ebbfc 189 // build out the response and send...
ansond 5:a929d65eb385 190 this->logger()->log("resource(PUT) completed for [%s]...",key.c_str());
ansond 21:8487990a3baa 191 coap_res_ptr = sn_coap_build_response(received_coap_ptr,COAP_MSG_CODE_RESPONSE_CHANGED);
ansond 21:8487990a3baa 192 sn_nsdl_send_coap_message(address,coap_res_ptr);
sam_grove 2:853f9ecc12df 193 } else {
ansond 5:a929d65eb385 194 this->logger()->log("ERROR: resource(PUT) mask is munged (mask: 0x%x)",this->m_res_mask);
ansond 0:b438482ebbfc 195 }
sam_grove 2:853f9ecc12df 196 } else {
ansond 5:a929d65eb385 197 this->logger()->log("ERROR: Binder(PUT) payload is NULL...");
ansond 0:b438482ebbfc 198 }
ansond 21:8487990a3baa 199 } else if(received_coap_ptr->msg_code == COAP_MSG_CODE_REQUEST_POST) {
ansond 21:8487990a3baa 200 if(received_coap_ptr->payload_len > 0) {
ansond 21:8487990a3baa 201 // process the POST if we have registered a callback for it...
ansond 21:8487990a3baa 202 if ((this->m_res_mask&SN_GRS_POST_ALLOWED) != 0) {
ansond 21:8487990a3baa 203 // binder interacts only with strings
ansond 21:8487990a3baa 204 string value = this->coapDataToString(received_coap_ptr->payload_ptr,received_coap_ptr->payload_len);
ansond 21:8487990a3baa 205
ansond 21:8487990a3baa 206 // call the resource post() to set the resource value
ansond 21:8487990a3baa 207 this->logger()->log("Calling resource(POST) with [%s]=[%s]...",key.c_str(),value.c_str());
ansond 21:8487990a3baa 208 this->post(value);
ansond 21:8487990a3baa 209
ansond 21:8487990a3baa 210 // build out the response and send...
ansond 21:8487990a3baa 211 this->logger()->log("resource(POST) completed for [%s]...",key.c_str());
ansond 21:8487990a3baa 212 coap_res_ptr = sn_coap_build_response(received_coap_ptr,COAP_MSG_CODE_RESPONSE_CHANGED);
ansond 21:8487990a3baa 213 sn_nsdl_send_coap_message(address,coap_res_ptr);
ansond 21:8487990a3baa 214 } else {
ansond 21:8487990a3baa 215 this->logger()->log("ERROR: resource(POST) mask is munged (mask: 0x%x)",this->m_res_mask);
ansond 21:8487990a3baa 216 }
ansond 21:8487990a3baa 217 } else {
ansond 21:8487990a3baa 218 this->logger()->log("ERROR: Binder(POST) payload is NULL...");
ansond 21:8487990a3baa 219 }
ansond 21:8487990a3baa 220 } else if(received_coap_ptr->msg_code == COAP_MSG_CODE_REQUEST_DELETE) {
ansond 21:8487990a3baa 221 if(received_coap_ptr->payload_len > 0) {
ansond 21:8487990a3baa 222 // process the DELETE if we have registered a callback for it...
ansond 21:8487990a3baa 223 if ((this->m_res_mask&SN_GRS_DELETE_ALLOWED) != 0) {
ansond 21:8487990a3baa 224 // binder interacts only with strings
ansond 21:8487990a3baa 225 string value = this->coapDataToString(received_coap_ptr->payload_ptr,received_coap_ptr->payload_len);
ansond 21:8487990a3baa 226
ansond 21:8487990a3baa 227 // call the resource del() to set the resource value
ansond 21:8487990a3baa 228 this->logger()->log("Calling resource(DELETE) with [%s]=[%s]...",key.c_str(),value.c_str());
ansond 21:8487990a3baa 229 this->del(value);
ansond 21:8487990a3baa 230
ansond 21:8487990a3baa 231 // build out the response and send...
ansond 21:8487990a3baa 232 this->logger()->log("resource(DELETE) completed for [%s]...",key.c_str());
ansond 21:8487990a3baa 233 coap_res_ptr = sn_coap_build_response(received_coap_ptr,COAP_MSG_CODE_RESPONSE_CHANGED);
ansond 21:8487990a3baa 234 sn_nsdl_send_coap_message(address,coap_res_ptr);
ansond 21:8487990a3baa 235 } else {
ansond 21:8487990a3baa 236 this->logger()->log("ERROR: resource(DELETE) mask is munged (mask: 0x%x)",this->m_res_mask);
ansond 21:8487990a3baa 237 }
ansond 21:8487990a3baa 238 } else {
ansond 21:8487990a3baa 239 this->logger()->log("ERROR: Binder(DELETE) payload is NULL...");
ansond 21:8487990a3baa 240 }
ansond 0:b438482ebbfc 241 }
ansond 0:b438482ebbfc 242
ansond 0:b438482ebbfc 243 sn_coap_parser_release_allocated_coap_msg_mem(coap_res_ptr);
ansond 22:192b598ba389 244
ansond 0:b438482ebbfc 245 return 0;
sam_grove 2:853f9ecc12df 246 }
sam_grove 2:853f9ecc12df 247
ansond 21:8487990a3baa 248 // send the notification
ansond 21:8487990a3baa 249 int DynamicResource::notify(const string data) {
ansond 21:8487990a3baa 250 return this->notify((uint8_t *)data.c_str(),(int)data.length());
ansond 21:8487990a3baa 251 }
ansond 21:8487990a3baa 252
ansond 21:8487990a3baa 253 // send the notification
ansond 21:8487990a3baa 254 int DynamicResource::notify(uint8_t *data,int data_length) {
ansond 23:caa0260acc21 255 int status = sn_nsdl_send_observation_notification(this->m_obs_token_ptr,this->m_obs_token_len,data,data_length,&this->m_obs_number,1,COAP_MSG_TYPE_NON_CONFIRMABLE,0);
ansond 21:8487990a3baa 256 if (status == 0) {
ansond 21:8487990a3baa 257 this->logger()->log("ERROR: resource(NOTIFY) send failed...");
ansond 21:8487990a3baa 258 }
ansond 21:8487990a3baa 259 return status;
ansond 21:8487990a3baa 260 }
ansond 21:8487990a3baa 261
sam_grove 2:853f9ecc12df 262 // default PUT (does nothing)
sam_grove 2:853f9ecc12df 263 void DynamicResource::put(const string value)
sam_grove 2:853f9ecc12df 264 {
sam_grove 2:853f9ecc12df 265 // not used by default
sam_grove 2:853f9ecc12df 266 ;
sam_grove 2:853f9ecc12df 267 }
sam_grove 2:853f9ecc12df 268
ansond 21:8487990a3baa 269 // default POST (does nothing)
ansond 21:8487990a3baa 270 void DynamicResource::post(const string value)
ansond 21:8487990a3baa 271 {
ansond 21:8487990a3baa 272 // not used by default
ansond 21:8487990a3baa 273 ;
ansond 21:8487990a3baa 274 }
ansond 21:8487990a3baa 275
ansond 21:8487990a3baa 276 // default DELETE (does nothing)
ansond 21:8487990a3baa 277 void DynamicResource::del(const string value)
ansond 21:8487990a3baa 278 {
ansond 21:8487990a3baa 279 // not used by default
ansond 21:8487990a3baa 280 ;
ansond 21:8487990a3baa 281 }
ansond 21:8487990a3baa 282
sam_grove 2:853f9ecc12df 283 // convenience method to get the URI from its buffer field...
sam_grove 2:853f9ecc12df 284 string DynamicResource::coapDataToString(uint8_t *coap_data_ptr,int coap_data_ptr_length)
sam_grove 2:853f9ecc12df 285 {
ansond 0:b438482ebbfc 286 if (coap_data_ptr != NULL && coap_data_ptr_length > 0) {
ansond 24:a6915e19814e 287 if (this->getDataWrapper() != NULL) {
ansond 24:a6915e19814e 288 // unwrap the data...
ansond 24:a6915e19814e 289 this->getDataWrapper()->unwrap(coap_data_ptr,coap_data_ptr_length);
ansond 24:a6915e19814e 290 char *buf = (char *)this->getDataWrapper()->get(); // assumes data is null terminated in DataWrapper...
ansond 24:a6915e19814e 291 return string(buf);
ansond 24:a6915e19814e 292 }
ansond 24:a6915e19814e 293 else {
ansond 24:a6915e19814e 294 // no unwrap of the data...
ansond 24:a6915e19814e 295 char buf[MAX_VALUE_BUFFER_LENGTH+1];
ansond 24:a6915e19814e 296 memset(buf,0,MAX_VALUE_BUFFER_LENGTH+1);
ansond 24:a6915e19814e 297 memcpy(buf,(char *)coap_data_ptr,coap_data_ptr_length);
ansond 24:a6915e19814e 298 return string(buf);
ansond 24:a6915e19814e 299 }
ansond 0:b438482ebbfc 300 }
ansond 0:b438482ebbfc 301 return string("");
sam_grove 2:853f9ecc12df 302 }