Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: XPL-App4_cleanup XPL-App5
xPL.cpp
00001 /* 00002 * xPL.Arduino v0.1, xPL Implementation for Arduino 00003 * 00004 * This code is parsing a xPL message stored in 'received' buffer 00005 * - isolate and store in 'line' buffer each part of the message -> detection of EOL character (DEC 10) 00006 * - analyse 'line', function of its number and store information in xpl_header memory 00007 * - check for each step if the message respect xPL protocol 00008 * - parse each command line 00009 * 00010 * Copyright (C) 2012 johan@pirlouit.ch, olivier.lebrun@gmail.com 00011 * Original version by Gromain59@gmail.com 00012 * 00013 * This program is free software; you can redistribute it and/or 00014 * modify it under the terms of the GNU General Public License 00015 * as published by the Free Software Foundation; either version 2 00016 * of the License, or (at your option) any later version. 00017 * 00018 * This program is distributed in the hope that it will be useful, 00019 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00021 * GNU General Public License for more details. 00022 * 00023 * You should have received a copy of the GNU General Public License 00024 * along with this program; if not, write to the Free Software 00025 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00026 */ 00027 00028 #include "xPL.h" 00029 00030 #define XPL_LINE_MESSAGE_BUFFER_MAX 128 // max length of a line // maximum command in a xpl message 00031 #define XPL_END_OF_LINE 10 00032 00033 // define the line number identifier 00034 #define XPL_MESSAGE_TYPE_IDENTIFIER 1 00035 #define XPL_OPEN_HEADER 2 00036 #define XPL_HOP_COUNT 3 00037 #define XPL_SOURCE 4 00038 #define XPL_TARGET 5 00039 #define XPL_CLOSE_HEADER 6 00040 #define XPL_SCHEMA_IDENTIFIER 7 00041 #define XPL_OPEN_SCHEMA 8 00042 00043 // Heartbeat request class definition 00044 //prog_char XPL_HBEAT_REQUEST_CLASS_ID[] PROGMEM = "hbeat"; 00045 //prog_char XPL_HBEAT_REQUEST_TYPE_ID[] PROGMEM = "request"; 00046 //prog_char XPL_HBEAT_ANSWER_CLASS_ID[] PROGMEM = "hbeat"; 00047 //prog_char XPL_HBEAT_ANSWER_TYPE_ID[] PROGMEM = "basic"; //app, basic 00048 #define XPL_HBEAT_REQUEST_CLASS_ID "hbeat" 00049 #define XPL_HBEAT_REQUEST_TYPE_ID "request" 00050 #define XPL_HBEAT_ANSWER_CLASS_ID "hbeat" 00051 #define XPL_HBEAT_ANSWER_TYPE_ID "app" 00052 00053 /* xPL Class */ 00054 xPL::xPL() 00055 { 00056 udp_port = XPL_UDP_PORT; 00057 00058 SendExternal = NULL; 00059 00060 #ifdef ENABLE_PARSING 00061 AfterParseAction = NULL; 00062 00063 last_heartbeat = 0; 00064 hbeat_interval = XPL_DEFAULT_HEARTBEAT_INTERVAL; 00065 xpl_accepted = XPL_ACCEPT_ALL; 00066 #endif 00067 } 00068 00069 xPL::~xPL() 00070 { 00071 } 00072 00073 /// Set the source of outgoing xPL messages 00074 void xPL::SetSource(const char * _vendorId, const char * _deviceId, const char * _instanceId) 00075 { 00076 memcpy(source.vendor_id, _vendorId, XPL_VENDOR_ID_MAX); 00077 memcpy(source.device_id, _deviceId, XPL_DEVICE_ID_MAX); 00078 memcpy(source.instance_id, _instanceId, XPL_INSTANCE_ID_MAX); 00079 } 00080 00081 /** 00082 * \brief Send an xPL message 00083 * \details There is no validation of the message, it is sent as is. 00084 * \param buffer buffer containing the xPL message. 00085 */ 00086 void xPL::SendMessage(char *_buffer) 00087 { 00088 (*SendExternal)(_buffer); 00089 } 00090 00091 /** 00092 * \brief Send an xPL message 00093 * \details There is no validation of the message, it is sent as is. 00094 * \param message An xPL message. 00095 * \param _useDefaultSource if true, insert the default source (defined in SetSource) on the message. 00096 */ 00097 void xPL::SendMessage(xPL_Message *_message, bool _useDefaultSource) 00098 { 00099 if(_useDefaultSource) 00100 { 00101 _message->SetSource(source.vendor_id, source.device_id, source.instance_id); 00102 } 00103 00104 SendMessage(_message->toString()); 00105 } 00106 00107 #ifdef ENABLE_PARSING 00108 00109 /** 00110 * \brief xPL Stuff 00111 * \details Send heartbeat messages at "hbeat_interval" interval 00112 */ 00113 void xPL::Process() 00114 { 00115 static bool bFirstRun = true; 00116 00117 // Check heartbeat + send 00118 //if ((millis()-last_heartbeat >= (unsigned long)hbeat_interval * 1000) 00119 // || (bFirstRun && millis() > 3000)) 00120 if ((clock()-last_heartbeat >= (unsigned long)hbeat_interval * 1000) 00121 || (bFirstRun && clock() > 3000)) 00122 { 00123 SendHBeat(); 00124 bFirstRun = false; 00125 } 00126 } 00127 00128 /** 00129 * \brief Parse an ingoing xPL message 00130 * \details Parse a message, check for hearbeat request and call user defined callback for post processing. 00131 * \param buffer buffer of the ingoing UDP Packet 00132 */ 00133 void xPL::ParseInputMessage(char* _buffer) 00134 { 00135 xPL_Message* xPLMessage = new xPL_Message(); 00136 Parse(xPLMessage, _buffer); 00137 00138 // check if the message is an hbeat.request to send a heartbeat 00139 if (CheckHBeatRequest(xPLMessage)) 00140 { 00141 SendHBeat(); 00142 } 00143 00144 // call the user defined callback to execute an action 00145 if(AfterParseAction != NULL) 00146 { 00147 (*AfterParseAction)(xPLMessage); 00148 } 00149 00150 delete xPLMessage; 00151 } 00152 00153 /** 00154 * \brief Check the xPL message target 00155 * \details Check if the xPL message is for us 00156 * \param _message an xPL message 00157 */ 00158 bool xPL::TargetIsMe(xPL_Message * _message) 00159 { 00160 if (memcmp(_message->target.vendor_id, source.vendor_id, strlen(source.vendor_id)) != 0) 00161 return false; 00162 00163 if (memcmp(_message->target.device_id, source.device_id, strlen(source.device_id)) != 0) 00164 return false; 00165 00166 if (memcmp(_message->target.instance_id, source.instance_id, strlen(source.instance_id)) != 0) 00167 return false; 00168 00169 return true; 00170 } 00171 00172 /** 00173 * \brief Send a heartbeat message 00174 */ 00175 void xPL::SendHBeat() 00176 { 00177 last_heartbeat = clock(); //millis(); 00178 char buffer[XPL_MESSAGE_BUFFER_MAX]; 00179 00180 // sprintf_P(buffer, PSTR("xpl-stat\n{\nhop=1\nsource=%s-%s.%s\ntarget=*\n}\n%s.%s\n{\ninterval=%d\n}\n"), source.vendor_id, source.device_id, source.instance_id, XPL_HBEAT_ANSWER_CLASS_ID, XPL_HBEAT_ANSWER_TYPE_ID, hbeat_interval); 00181 //sprintf(buffer, "xpl-stat\r\n{\r\nhop=1\r\nsource=%s-%s.%s\r\ntarget=*\r\n}\r\n%s.%s\r\n{\r\ninterval=%d\r\nport=3865\r\nremote-ip=8.8.8.8\r\nversion=1.0\r\n}\r\n", source.vendor_id, source.device_id, source.instance_id, XPL_HBEAT_ANSWER_CLASS_ID, XPL_HBEAT_ANSWER_TYPE_ID, hbeat_interval); 00182 sprintf(buffer, "xpl-stat\n{\nhop=1\nsource=%s-%s.%s\ntarget=*\n}\n%s.%s\n{\ninterval=%d\nport=3865\nremote-ip=8.8.8.8\nversion=1.0\n}\n", source.vendor_id, source.device_id, source.instance_id, XPL_HBEAT_ANSWER_CLASS_ID, XPL_HBEAT_ANSWER_TYPE_ID, hbeat_interval); 00183 00184 //(*SendExternal)(buffer); 00185 SendMessage(buffer); 00186 00187 printf("XPL: HB Sent\r\n"); 00188 } 00189 00190 /** 00191 * \brief Check if the message is a heartbeat request 00192 * \param _message an xPL message 00193 */ 00194 inline bool xPL::CheckHBeatRequest(xPL_Message* _message) 00195 { 00196 if (!TargetIsMe(_message)) 00197 return false; 00198 00199 return _message->IsSchema(XPL_HBEAT_REQUEST_CLASS_ID, XPL_HBEAT_REQUEST_TYPE_ID); 00200 } 00201 00202 /** 00203 * \brief Parse a buffer and generate a xPL_Message 00204 * \details Line based xPL parser 00205 * \param _xPLMessage the result xPL message 00206 * \param _message the buffer 00207 */ 00208 void xPL::Parse(xPL_Message* _xPLMessage, char* _buffer) 00209 { 00210 int len = strlen(_buffer); 00211 00212 short j=0; 00213 short line=0; 00214 int result=0; 00215 char lineBuffer[XPL_LINE_MESSAGE_BUFFER_MAX+1]; 00216 00217 // read each character of the message 00218 for(short i = 0; i < len; i++) 00219 { 00220 // load byte by byte in 'line' buffer, until '\n' is detected 00221 if(_buffer[i] == XPL_END_OF_LINE) // is it a linefeed (ASCII: 10 decimal) 00222 { 00223 ++line; 00224 lineBuffer[j]='\0'; // add the end of string id 00225 00226 if(line <= XPL_OPEN_SCHEMA) 00227 { 00228 // first part: header and schema determination 00229 // we analyse the line, function of the line number in the xpl message 00230 result = AnalyseHeaderLine(_xPLMessage, lineBuffer ,line); 00231 } 00232 00233 if(line > XPL_OPEN_SCHEMA) 00234 { 00235 // second part: command line 00236 // we analyse the specific command line, function of the line number in the xpl message 00237 result = AnalyseCommandLine(_xPLMessage, lineBuffer, line-9, j); 00238 00239 if(result == _xPLMessage->command_count+1) 00240 break; 00241 } 00242 00243 if (result < 0) break; 00244 00245 j = 0; // reset the buffer pointer 00246 clearStr(lineBuffer); // clear the buffer 00247 } 00248 else 00249 { 00250 // next character 00251 lineBuffer[j++] = _buffer[i]; 00252 } 00253 } 00254 } 00255 00256 /** 00257 * \brief Parse the header part of the xPL message line by line 00258 * \param _xPLMessage the result xPL message 00259 * \param _buffer the line to parse 00260 * \param _line the line number 00261 */ 00262 short xPL::AnalyseHeaderLine(xPL_Message* _xPLMessage, char* _buffer, short _line) 00263 { 00264 switch (_line) 00265 { 00266 case XPL_MESSAGE_TYPE_IDENTIFIER: //message type identifier 00267 00268 if (memcmp(_buffer,"xpl-",4)==0) //xpl 00269 { 00270 if (memcmp(_buffer+4,"cmnd",4)==0) //command type 00271 { 00272 _xPLMessage->type=XPL_CMND; //xpl-cmnd 00273 } 00274 else if (memcmp(_buffer+4,"stat",4)==0) //statut type 00275 { 00276 _xPLMessage->type=XPL_STAT; //xpl-stat 00277 } 00278 else if (memcmp(_buffer+4,"trig",4)==0) // trigger type 00279 { 00280 _xPLMessage->type=XPL_TRIG; //xpl-trig 00281 } 00282 } 00283 else 00284 { 00285 return 0; //unknown message 00286 } 00287 00288 return 1; 00289 00290 //break; 00291 00292 case XPL_OPEN_HEADER: //header begin 00293 00294 if (memcmp(_buffer,"{",1)==0) 00295 { 00296 return 2; 00297 } 00298 //else 00299 //{ 00300 return -2; 00301 //} 00302 00303 //break; 00304 00305 case XPL_HOP_COUNT: //hop 00306 if (sscanf(_buffer, XPL_HOP_COUNT_PARSER, &_xPLMessage->hop)) 00307 { 00308 return 3; 00309 } 00310 //else 00311 //{ 00312 return -3; 00313 //} 00314 00315 //break; 00316 00317 case XPL_SOURCE: //source 00318 if (sscanf(_buffer, XPL_SOURCE_PARSER, &_xPLMessage->source.vendor_id, &_xPLMessage->source.device_id, &_xPLMessage->source.instance_id) == 3) 00319 { 00320 return 4; 00321 } 00322 //else 00323 //{ 00324 return -4; 00325 //} 00326 00327 //break; 00328 00329 case XPL_TARGET: //target 00330 00331 if (sscanf(_buffer, XPL_TARGET_PARSER, &_xPLMessage->target.vendor_id, &_xPLMessage->target.device_id, &_xPLMessage->target.instance_id) == 3) 00332 { 00333 return 5; 00334 } 00335 //else 00336 //{ 00337 if(memcmp(_xPLMessage->target.vendor_id,"*", 1) == 0) // check if broadcast message 00338 { 00339 return 5; 00340 } 00341 //else 00342 //{ 00343 return -5; 00344 //} 00345 //} 00346 //break; 00347 00348 case XPL_CLOSE_HEADER: //header end 00349 if (memcmp(_buffer,"}",1)==0) 00350 { 00351 return 6; 00352 } 00353 //else 00354 //{ 00355 return -6; 00356 //} 00357 00358 //break; 00359 00360 case XPL_SCHEMA_IDENTIFIER: //schema 00361 sscanf(_buffer, XPL_SCHEMA_PARSER, &_xPLMessage->schema.class_id, &_xPLMessage->schema.type_id); 00362 return 7; 00363 00364 case XPL_OPEN_SCHEMA: //header begin 00365 if (memcmp(_buffer,"{",1)==0) 00366 { 00367 return 8; 00368 } 00369 //else 00370 //{ 00371 return -8; 00372 //} 00373 //break; 00374 } 00375 00376 return -100; 00377 } 00378 00379 /** 00380 * \brief Parse the body part of the xPL message line by line 00381 * \param _xPLMessage the result xPL message 00382 * \param _buffer the line to parse 00383 * \param _command_line the line number 00384 */ 00385 short xPL::AnalyseCommandLine(xPL_Message * _xPLMessage, char *_buffer, short _command_line, short line_length) 00386 { 00387 if (memcmp(_buffer,"}",1) == 0) // End of schema 00388 { 00389 return _xPLMessage->command_count+1; 00390 } 00391 else // parse the next command 00392 { 00393 struct_command newcmd; 00394 00395 sscanf(_buffer, XPL_COMMAND_PARSER, &newcmd.name, &newcmd.value); 00396 00397 _xPLMessage->AddCommand(newcmd.name, newcmd.value); 00398 00399 return _command_line; 00400 } 00401 } 00402 #endif
Generated on Thu Jul 28 2022 00:39:57 by
1.7.2