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.
dpv1.c
00001 /******************************************************************************* 00002 ******************************************************************************** 00003 ** ** 00004 ** ABCC Starter Kit version 2.01.01 (2015-12-14) ** 00005 ** ** 00006 ** Delivered with: ** 00007 ** ABCC Driver 4.01.01 (2015-12-14) ** 00008 ** ABP 7.16.01 (2015-10-14) ** 00009 ** */ 00010 /******************************************************************************* 00011 ******************************************************************************** 00012 ** COPYRIGHT NOTIFICATION (c) 2015 HMS Industrial Networks AB ** 00013 ** ** 00014 ** This code is the property of HMS Industrial Networks AB. ** 00015 ** The source code may not be reproduced, distributed, or used without ** 00016 ** permission. When used together with a product from HMS, permission is ** 00017 ** granted to modify, reproduce and distribute the code in binary form ** 00018 ** without any restrictions. ** 00019 ** ** 00020 ** THE CODE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. HMS DOES NOT ** 00021 ** WARRANT THAT THE FUNCTIONS OF THE CODE WILL MEET YOUR REQUIREMENTS, OR ** 00022 ** THAT THE OPERATION OF THE CODE WILL BE UNINTERRUPTED OR ERROR-FREE, OR ** 00023 ** THAT DEFECTS IN IT CAN BE CORRECTED. ** 00024 ******************************************************************************** 00025 ******************************************************************************** 00026 ** Source file for the PROFIBUS DPV1 IO Object. 00027 ******************************************************************************** 00028 ******************************************************************************** 00029 */ 00030 00031 #include "abcc_td.h" 00032 #include "abcc_sys_adapt.h" 00033 #include "abcc_obj_cfg.h" 00034 #include "abcc.h" 00035 #include "dpv1.h" 00036 #include "abp_dpv1.h" 00037 #include "string.h" 00038 #include "appl_abcc_handler.h" 00039 #include "abcc_port.h" 00040 00041 #if DPV1_OBJ_ENABLE 00042 00043 /******************************************************************************* 00044 ** Defines 00045 ******************************************************************************** 00046 */ 00047 00048 #define DPV1_PRM_STANDARD_BYTES 7 00049 #define DPV1_PRM_DPV1_STATUS_BYTES 3 00050 00051 /*------------------------------------------------------------------------------ 00052 ** Object attribute values 00053 **------------------------------------------------------------------------------ 00054 */ 00055 #define DPV1_OA_NAME_VALUE "PROFIBUS DP-V1" 00056 #define DPV1_OA_REV_VALUE 4 00057 #define DPV1_OA_NUM_INST_VALUE 1 00058 #define DPV1_OA_HIGHEST_INST_VALUE 1 00059 00060 /******************************************************************************* 00061 ** Typedefs 00062 ******************************************************************************** 00063 */ 00064 00065 /*------------------------------------------------------------------------------ 00066 ** dpv1_ObjectType. 00067 ** Structure describing an Profibus Object. 00068 **------------------------------------------------------------------------------ 00069 */ 00070 typedef struct dpv1_Object 00071 { 00072 const char* pcName; 00073 UINT8 bRevision; 00074 UINT16 iNumberOfInstances; 00075 UINT16 iHighestInstanceNo; 00076 } 00077 dpv1_ObjectType; 00078 00079 /*------------------------------------------------------------------------------ 00080 ** Structure describing the EtherNet/IP Instance 1 attributes. 00081 **------------------------------------------------------------------------------ 00082 */ 00083 #if DPV1_IA_PRM_DATA_ENABLE || \ 00084 DPV1_IA_EXPECTED_CFG_DATA_ENABLE || \ 00085 DPV1_IA_IM_HEADER_ENABLE 00086 typedef struct dpv1_Instance 00087 { 00088 #if DPV1_IA_PRM_DATA_ENABLE 00089 UINT8 abParamData[ DPV1_IA_PRM_DATA_ARRAY_SIZE ]; 00090 #endif 00091 #if DPV1_IA_EXPECTED_CFG_DATA_ENABLE 00092 UINT8 abExpectedCfgData[ DPV1_IA_EXPECTED_CFG_DATA_ARRAY_SIZE ]; 00093 #endif 00094 #if DPV1_IA_IM_HEADER_ENABLE 00095 UINT8 abIMheader[ DPV1_IA_IM_HEADER_ARRAY_SIZE]; 00096 #endif 00097 } 00098 dpv1_InstanceType; 00099 #endif 00100 00101 /*------------------------------------------------------------------------------ 00102 ** Forward declarations 00103 **------------------------------------------------------------------------------ 00104 */ 00105 static void InstanceCommand( ABP_MsgType* psNewMessage ); 00106 static void ObjectCommand( ABP_MsgType* psNewMessage ); 00107 00108 /******************************************************************************* 00109 ** Private Globals 00110 ******************************************************************************** 00111 */ 00112 00113 static const dpv1_ObjectType dpv1_sObject = 00114 { 00115 DPV1_OA_NAME_VALUE, /* Name. */ 00116 DPV1_OA_REV_VALUE, /* Revision. */ 00117 DPV1_OA_NUM_INST_VALUE, /* Number of instances. */ 00118 DPV1_OA_HIGHEST_INST_VALUE /* Highest instance number. */ 00119 }; 00120 00121 #if DPV1_IA_PRM_DATA_ENABLE || \ 00122 DPV1_IA_EXPECTED_CFG_DATA_ENABLE || \ 00123 DPV1_IA_IM_HEADER_ENABLE 00124 static dpv1_InstanceType dpv1_sInstance = 00125 { 00126 #if DPV1_IA_PRM_DATA_ENABLE 00127 DPV1_IA_PRM_DATA_VALUE, 00128 #endif 00129 #if DPV1_IA_EXPECTED_CFG_DATA_ENABLE 00130 DPV1_IA_EXPECTED_CFG_DATA_VALUE, 00131 #endif 00132 #if DPV1_IA_IM_HEADER_ENABLE 00133 DPV1_IA_IM_HEADER_VALUE, 00134 #endif 00135 }; 00136 #endif 00137 00138 /******************************************************************************* 00139 ** Public Services 00140 ******************************************************************************** 00141 */ 00142 00143 void DPV1_ProcessCmdMsg( ABP_MsgType* psNewMessage ) 00144 { 00145 /* 00146 ** This function processes commands to the Profibus Object and it's Instance. 00147 */ 00148 if( ABCC_GetMsgInstance( psNewMessage ) == ABP_INST_OBJ ) 00149 { 00150 /* 00151 ** Profibus object Command 00152 */ 00153 ObjectCommand( psNewMessage ); 00154 } 00155 else 00156 { 00157 /* 00158 ** Profibus instance Command 00159 */ 00160 InstanceCommand( psNewMessage ); 00161 } 00162 00163 ABCC_SendRespMsg( psNewMessage ); 00164 } 00165 00166 00167 /******************************************************************************* 00168 ** Private Services 00169 ******************************************************************************** 00170 */ 00171 00172 00173 /*------------------------------------------------------------------------------ 00174 ** Processes commands to DPV1 Instances 00175 **------------------------------------------------------------------------------ 00176 ** Arguments: 00177 ** psNewMessage - Pointer to a ABP_MsgType message. 00178 ** 00179 ** Returns: 00180 ** None 00181 **------------------------------------------------------------------------------ 00182 */ 00183 static void InstanceCommand( ABP_MsgType* psNewMessage ) 00184 { 00185 /* 00186 ** This function processes commands to the Profibus Instance. 00187 */ 00188 if( ABCC_GetMsgInstance( psNewMessage ) != 1 ) 00189 { 00190 /* 00191 ** The Instance does not exist. 00192 */ 00193 ABP_SetMsgErrorResponse( psNewMessage, 1, ABP_ERR_UNSUP_INST ); 00194 return; 00195 } 00196 00197 switch ( ABCC_GetMsgCmdBits( psNewMessage ) ) 00198 { 00199 case ABP_CMD_GET_ATTR: 00200 { 00201 switch( ABCC_GetMsgCmdExt0( psNewMessage ) ) 00202 { 00203 #if DPV1_IA_IDENT_NUMBER_ENABLE 00204 case ABP_DPV1_IA_IDENT_NUMBER: 00205 00206 /* 00207 ** Copy the 1st Instance 1 attribute (Device ID) to the message. 00208 */ 00209 ABCC_SetMsgData16( psNewMessage, DPV1_IA_IDENT_NUMBER_VALUE, 0 ); 00210 ABP_SetMsgResponse( psNewMessage, ABP_DPV1_IA_IDENT_NUMBER_DS ); 00211 break; 00212 #endif 00213 #if DPV1_IA_PRM_DATA_ENABLE 00214 case ABP_DPV1_IA_PRM_DATA: 00215 00216 /* 00217 ** The Parameter data is not getable. It's only setable. 00218 */ 00219 ABP_SetMsgErrorResponse( psNewMessage, 1, ABP_ERR_ATTR_NOT_GETABLE ); 00220 break; 00221 #endif 00222 #if DPV1_IA_EXPECTED_CFG_DATA_ENABLE 00223 case ABP_DPV1_IA_EXPECTED_CFG_DATA: 00224 { 00225 UINT16 iIndex; 00226 00227 /* 00228 ** Copy the 3rd Instance 1 attribute (Expected configuration) to the message. 00229 */ 00230 for( iIndex = 0; iIndex < DPV1_IA_EXPECTED_CFG_DATA_ARRAY_SIZE; iIndex++ ) 00231 { 00232 ABCC_SetMsgData8( psNewMessage, dpv1_sInstance.abExpectedCfgData[ iIndex ], iIndex ); 00233 } 00234 ABP_SetMsgResponse( psNewMessage, DPV1_IA_EXPECTED_CFG_DATA_ARRAY_SIZE ); 00235 break; 00236 } 00237 #endif 00238 #if DPV1_IA_SSA_ENABLED_ENABLE 00239 case ABP_DPV1_IA_SSA_ENABLED: 00240 00241 /* 00242 ** Copy the 4th Instance 1 attribute (SSA enabled) to the message. 00243 */ 00244 ABCC_SetMsgData8( psNewMessage, DPV1_IA_SSA_ENABLED_VALUE, 0 ); 00245 ABP_SetMsgResponse( psNewMessage, ABP_DPV1_IA_SSA_ENABLED_DS ); 00246 break; 00247 #endif 00248 #if DPV1_IA_MANUFACTURER_ID_ENABLE 00249 case ABP_DPV1_IA_MANUFACTURER_ID: 00250 00251 /* 00252 ** Copy the 8th Instance 1 attribute (Manufacturer ID) to the message. 00253 */ 00254 ABCC_SetMsgData16( psNewMessage, DPV1_IA_MANUFACTURER_ID_VALUE, 0 ); 00255 ABP_SetMsgResponse( psNewMessage, ABP_DPV1_IA_MANUFACTURER_ID_DS ); 00256 break; 00257 #endif 00258 #if DPV1_IA_ORDER_ID_ENABLE 00259 case ABP_DPV1_IA_ORDER_ID: 00260 { 00261 UINT16 iStrLength; 00262 00263 iStrLength = (UINT16)strlen( DPV1_IA_ORDER_ID_VALUE ); 00264 00265 /* 00266 ** The maximum number of elements allowed in the array should not be 00267 ** checked here. It should be checked earlier... 00268 */ 00269 if( iStrLength > ABP_DPV1_IA_ORDER_ID_DS ) 00270 { 00271 iStrLength = ABP_DPV1_IA_ORDER_ID_DS; 00272 } 00273 00274 /* 00275 ** Copy the 9th Instance 1 attribute (IM Order ID) to the message. 00276 */ 00277 ABCC_SetMsgString( psNewMessage, DPV1_IA_ORDER_ID_VALUE, iStrLength, 0 ); 00278 ABP_SetMsgResponse( psNewMessage, iStrLength ); 00279 break; 00280 } 00281 #endif 00282 #if DPV1_IA_SERIAL_NO_ENABLE 00283 case ABP_DPV1_IA_SERIAL_NO: 00284 { 00285 UINT16 iStrLength; 00286 00287 iStrLength = (UINT16)strlen( DPV1_IA_SERIAL_NO_VALUE ); 00288 00289 /* 00290 ** The maximum number of elements allowed in the array should not be 00291 ** checked here. It should be checked earlier... 00292 */ 00293 if( iStrLength > ABP_DPV1_IA_SERIAL_NO_DS ) 00294 { 00295 iStrLength = ABP_DPV1_IA_SERIAL_NO_DS; 00296 } 00297 00298 /* 00299 ** Copy the 10th Instance 1 attribute (IM Serial number) to the message. 00300 */ 00301 ABCC_SetMsgString( psNewMessage, DPV1_IA_SERIAL_NO_VALUE, iStrLength, 0 ); 00302 ABP_SetMsgResponse( psNewMessage, iStrLength ); 00303 break; 00304 } 00305 #endif 00306 #if DPV1_IA_HW_REV_ENABLE 00307 case ABP_DPV1_IA_HW_REV: 00308 00309 /* 00310 ** Copy the 11th Instance 1 attribute (IM Hardware revision) to the message. 00311 */ 00312 ABCC_SetMsgData16( psNewMessage, DPV1_IA_HW_REV_VALUE, 0 ); 00313 ABP_SetMsgResponse( psNewMessage, ABP_DPV1_IA_HW_REV_DS ); 00314 break; 00315 #endif 00316 #if DPV1_IA_SW_REV_ENABLE 00317 case ABP_DPV1_IA_SW_REV: 00318 00319 /* 00320 ** Copy the 12th Instance 1 attribute (Software revision) to the message. 00321 */ 00322 ABCC_SetMsgData8( psNewMessage, DPV1_IA_SW_REV_SYMBOL_VALUE, 0 ); 00323 ABCC_SetMsgData8( psNewMessage, DPV1_IA_SW_REV_MAJOR_VALUE, 1 ); 00324 ABCC_SetMsgData8( psNewMessage, DPV1_IA_SW_REV_MINOR_VALUE, 2 ); 00325 ABCC_SetMsgData8( psNewMessage, DPV1_IA_SW_REV_BUILD_VALUE, 3 ); 00326 00327 ABP_SetMsgResponse( psNewMessage, ABP_DPV1_IA_SW_REV_DS ); 00328 break; 00329 #endif 00330 #if DPV1_IA_REV_COUNTER_ENABLE 00331 case ABP_DPV1_IA_REV_COUNTER: 00332 00333 /* 00334 ** Copy the 13th Instance 1 attribute (Revision counter) to the message. 00335 */ 00336 ABCC_SetMsgData16( psNewMessage, DPV1_IA_REV_COUNTER_VALUE, 0 ); 00337 ABP_SetMsgResponse( psNewMessage, ABP_DPV1_IA_REV_COUNTER_DS ); 00338 break; 00339 #endif 00340 #if DPV1_IA_PROFILE_ID_ENABLE 00341 case ABP_DPV1_IA_PROFILE_ID: 00342 00343 /* 00344 ** Copy the 14th Instance 1 attribute (Profile ID) to the message. 00345 */ 00346 ABCC_SetMsgData16( psNewMessage, DPV1_IA_PROFILE_ID_VALUE, 0 ); 00347 ABP_SetMsgResponse( psNewMessage, ABP_DPV1_IA_PROFILE_ID_DS ); 00348 break; 00349 #endif 00350 #if DPV1_IA_PROFILE_SPEC_TYPE_ENABLE 00351 case ABP_DPV1_IA_PROFILE_SPEC_TYPE: 00352 00353 /* 00354 ** Copy the 15th Instance 1 attribute (Profile specific type) to the message. 00355 */ 00356 ABCC_SetMsgData16( psNewMessage, DPV1_IA_PROFILE_SPEC_TYPE_VALUE, 0 ); 00357 ABP_SetMsgResponse( psNewMessage, ABP_DPV1_IA_PROFILE_SPEC_TYPE_DS ); 00358 break; 00359 #endif 00360 #if DPV1_IA_IM_HEADER_ENABLE 00361 case ABP_DPV1_IA_IM_HEADER: 00362 { 00363 UINT16 iIndex; 00364 00365 /* 00366 ** Copy the 18th Instance 1 attribute (IM header) to the message. 00367 */ 00368 for( iIndex = 0; iIndex < DPV1_IA_IM_HEADER_ARRAY_SIZE; iIndex++ ) 00369 { 00370 ABCC_SetMsgData8( psNewMessage, dpv1_sInstance.abIMheader[ iIndex ], iIndex ); 00371 } 00372 ABP_SetMsgResponse( psNewMessage, DPV1_IA_IM_HEADER_ARRAY_SIZE ); 00373 break; 00374 } 00375 #endif 00376 default: 00377 00378 /* 00379 ** Unsupported attribute. 00380 */ 00381 ABP_SetMsgErrorResponse( psNewMessage, 1, ABP_ERR_INV_CMD_EXT_0 ); 00382 break; 00383 } 00384 break; 00385 } 00386 #if DPV1_IA_PRM_DATA_ENABLE || DPV1_IA_EXPECTED_CFG_DATA_ENABLE 00387 case ABP_CMD_SET_ATTR: 00388 { 00389 switch( ABCC_GetMsgCmdExt0( psNewMessage ) ) 00390 { 00391 #if DPV1_IA_PRM_DATA_ENABLE 00392 case ABP_DPV1_IA_PRM_DATA: 00393 00394 /* 00395 ** The data size is verified. 00396 */ 00397 if( psNewMessage->sHeader.iDataSize > DPV1_IA_PRM_DATA_ARRAY_SIZE ) 00398 { 00399 ABP_SetMsgErrorResponse( psNewMessage, 1, ABP_ERR_TOO_MUCH_DATA ); 00400 } 00401 else if( psNewMessage->sHeader.iDataSize < DPV1_PRM_STANDARD_BYTES ) 00402 { 00403 ABP_SetMsgErrorResponse( psNewMessage, 1, ABP_ERR_NOT_ENOUGH_DATA ); 00404 } 00405 else 00406 { 00407 /* 00408 ** If the application has defined user specific parameter data, 00409 ** this is the place to check the data (from byte 10 and forth) 00410 ** In this example we only update the attribute. 00411 */ 00412 ABCC_PORT_MemCpy( dpv1_sInstance.abParamData, psNewMessage->abData, DPV1_PRM_STANDARD_BYTES + DPV1_PRM_DPV1_STATUS_BYTES ); 00413 00414 ABP_SetMsgResponse( psNewMessage, 0 ); 00415 } 00416 break; 00417 #endif 00418 #if DPV1_IA_EXPECTED_CFG_DATA_ENABLE 00419 case ABP_DPV1_IA_EXPECTED_CFG_DATA: 00420 00421 /* 00422 ** The data size is verified. 00423 */ 00424 if( psNewMessage->sHeader.iDataSize > DPV1_IA_EXPECTED_CFG_DATA_ARRAY_SIZE ) 00425 { 00426 ABP_SetMsgErrorResponse( psNewMessage, 1, ABP_ERR_TOO_MUCH_DATA ); 00427 } 00428 else 00429 { 00430 ABCC_PORT_MemCpy( dpv1_sInstance.abExpectedCfgData, psNewMessage->abData, psNewMessage->sHeader.iDataSize ); 00431 00432 ABP_SetMsgResponse( psNewMessage, 0 ); 00433 } 00434 break; 00435 #endif 00436 00437 #if DPV1_IA_IDENT_NUMBER_ENABLE 00438 case ABP_DPV1_IA_IDENT_NUMBER: 00439 #endif 00440 #if DPV1_IA_SSA_ENABLED_ENABLE 00441 case ABP_DPV1_IA_SSA_ENABLED: 00442 #endif 00443 #if DPV1_IA_MANUFACTURER_ID_ENABLE 00444 case ABP_DPV1_IA_MANUFACTURER_ID: 00445 #endif 00446 #if DPV1_IA_ORDER_ID_ENABLE 00447 case ABP_DPV1_IA_ORDER_ID: 00448 #endif 00449 #if DPV1_IA_SERIAL_NO_ENABLE 00450 case ABP_DPV1_IA_SERIAL_NO: 00451 #endif 00452 #if DPV1_IA_HW_REV_ENABLE 00453 case ABP_DPV1_IA_HW_REV: 00454 #endif 00455 #if DPV1_IA_SW_REV_ENABLE 00456 case ABP_DPV1_IA_SW_REV: 00457 #endif 00458 #if DPV1_IA_REV_COUNTER_ENABLE 00459 case ABP_DPV1_IA_REV_COUNTER: 00460 #endif 00461 #if DPV1_IA_PROFILE_ID_ENABLE 00462 case ABP_DPV1_IA_PROFILE_ID: 00463 #endif 00464 #if DPV1_IA_PROFILE_SPEC_TYPE_ENABLE 00465 case ABP_DPV1_IA_PROFILE_SPEC_TYPE: 00466 #endif 00467 /* 00468 ** Unsettable attribute. 00469 */ 00470 ABP_SetMsgErrorResponse( psNewMessage, 1, ABP_ERR_ATTR_NOT_SETABLE ); 00471 break; 00472 00473 default: 00474 00475 /* 00476 ** Unsupported attribute. 00477 */ 00478 ABP_SetMsgErrorResponse( psNewMessage, 1, ABP_ERR_INV_CMD_EXT_0 ); 00479 break; 00480 } 00481 } 00482 #endif 00483 default: 00484 00485 /* 00486 ** Unsupported command. 00487 */ 00488 ABP_SetMsgErrorResponse( psNewMessage, 1, ABP_ERR_UNSUP_CMD ); 00489 break; 00490 00491 } /* End of switch( Command number ) */ 00492 } 00493 00494 /*------------------------------------------------------------------------------ 00495 ** Processes commands to the DPV1 Object (Instance 0) 00496 **------------------------------------------------------------------------------ 00497 ** Arguments: 00498 ** psNewMessage - Pointer to a ABP_MsgType message. 00499 ** 00500 ** Returns: 00501 ** None 00502 **------------------------------------------------------------------------------ 00503 */ 00504 static void ObjectCommand( ABP_MsgType* psNewMessage ) 00505 { 00506 /* 00507 ** This function processes commands to the Profibus Object (Instance 0). 00508 */ 00509 switch ( ABCC_GetMsgCmdBits( psNewMessage ) ) 00510 { 00511 case ABP_CMD_GET_ATTR: 00512 { 00513 switch( ABCC_GetMsgCmdExt0( psNewMessage ) ) 00514 { 00515 case ABP_OA_NAME: 00516 { 00517 UINT16 iStrLength; 00518 00519 /* 00520 ** Copy the attribute to a message. 00521 */ 00522 iStrLength = (UINT16)strlen( dpv1_sObject.pcName ); 00523 ABCC_SetMsgString( psNewMessage, dpv1_sObject.pcName, iStrLength, 0 ); 00524 ABP_SetMsgResponse( psNewMessage, (UINT8)iStrLength ); 00525 break; 00526 } 00527 00528 case ABP_OA_REV: 00529 00530 /* 00531 ** Copy the attribute to a message. 00532 */ 00533 ABCC_SetMsgData8( psNewMessage, dpv1_sObject.bRevision, 0 ); 00534 ABP_SetMsgResponse( psNewMessage, ABP_OA_REV_DS ); 00535 break; 00536 00537 case ABP_OA_NUM_INST: 00538 00539 /* 00540 ** Copy the attribute to a message. 00541 */ 00542 ABCC_SetMsgData16( psNewMessage, dpv1_sObject.iNumberOfInstances, 0 ); 00543 ABP_SetMsgResponse( psNewMessage, ABP_OA_NUM_INST_DS ); 00544 break; 00545 00546 case ABP_OA_HIGHEST_INST: 00547 00548 /* 00549 ** Copy the attribute to a message. 00550 */ 00551 ABCC_SetMsgData16( psNewMessage, dpv1_sObject.iHighestInstanceNo, 0 ); 00552 ABP_SetMsgResponse( psNewMessage, ABP_OA_HIGHEST_INST_DS ); 00553 break; 00554 00555 default: 00556 00557 /* 00558 ** Unsupported attribute. 00559 */ 00560 ABP_SetMsgErrorResponse( psNewMessage, 1, ABP_ERR_INV_CMD_EXT_0 ); 00561 break; 00562 } 00563 break; 00564 } 00565 00566 default: 00567 00568 /* 00569 ** Unsupported command. 00570 */ 00571 ABP_SetMsgErrorResponse( psNewMessage, 1, ABP_ERR_UNSUP_CMD ); 00572 break; 00573 00574 } /* End of switch( Command number ) */ 00575 } 00576 00577 #endif /* DPV1_OBJ_ENABLE */
Generated on Tue Jul 12 2022 15:51:57 by
