When requesting an information model, a complete accumulation of useful data (PDU) in RAM occurs, and then parsing of all objects. At the same time, my project does not have enough memory to store the entire buffer of useful data. Is there a way to parse objects one at a time with freeing useful data (PDU) from the buffer?
Reading an information model is a project requirement. I see the output only in object-by-object parsing of the model with clearing the processed part of the buffer.
Is it possible to change the program to implement the following algorithm: Receive the next portion of useful data (PDU), start the dlms_getValueFromData function, if at least one whole object is received, then we clear the processed useful data buffer and the procedure is repeated.?
Can you use malloc with your compiler? We have use cl_parseObjectCount and cl_parseNextObject methods when we have use compiler where is no malloc. Get the latest version and try this:
BR,
Mikko
int ret;
message data;
gxReplyData reply;
mes_init(&data);
reply_init(&reply);
unsigned short pos, count;
gxObject obj;
//We are not interested from the access modes.
//If you want to know the access modes allocate enought space for them.
obj.access = NULL;
if ((ret = cl_getObjectsRequest(&connection->settings, &data)) != 0 ||
(ret = com_readDataBlock(connection, &data, &reply)) != 0 ||
(ret = cl_parseObjectCount(&reply.data, &count)) != 0)
{
printf("GetObjects failed %s\r\n", hlp_getErrorMessage(ret));
}
if (ret == 0)
{
for (pos = 0; pos != count; ++pos)
{
if ((ret = cl_parseNextObject(&reply.data, &obj)) != 0)
{
break;
}
//Read object data.
///////////////////////////////////////////////////////////////////////////////////
// Profile generics are read later because they are special cases.
// (There might be so lots of data and we so not want waste time to read all the data.)
if (obj.objectType == DLMS_OBJECT_TYPE_PROFILE_GENERIC)
{
continue;
}
}
}
This is supported by other programming languages, but we are removing support for this at some point. This is not possible to use when ciphering is used and companies are moving to start use ciphering.
Can you use malloc with your compiler? - yes. Maybe it makes sense to abandon the dynamic allocation of memory? But then how to properly configure static buffers?
It's great if you can use malloc because it makes everything easier. If you can't use malloc, you need to use static buffers and it's a pain when you are reading historical (profile generics) or complex data.
Your solution allows to parse objects after receiving a full buffer of useful data (PDU). The my problem is getting the entire amount of data of the info model from the counter in the com_readDataBlock function. Is it possible to parse objects "on the fly" parsing the response from the meter?
It's not possible to parse objects "on the fly". You need to read all data. Is this problem with historical data (Profile Generics). If it is, you can use readByEntry and readByRange methods to read data from the buffer example day by day.
Good afternoon! My task is to obtain an information model in the form of a table containing only class types and obis codes and store it in the microcontroller's RAM. I do not need to have fully filled objects. Maybe this can be somehow implemented without parsing a completely read buffer, and checking for the presence of an entire object in the buffer, parse it and clear the buffer, read on?
for (; pos != count; ++pos)
{
reply.data.position = 0;
if ((ret = cl_parseNextObject(&reply.data, &obj)) != 0)
{
reply.data.position = reply.data.size;
break;
}
else
{
// It never goes into this part of the code. And the memory is not freed
bb_trim(&reply.data);
}
}
from this code: //Send data.
if ((ret = readDLMSPacket(connection, data.data[0], &reply)) != DLMS_ERROR_CODE_OK)
{
return ret;
}
//Get object count on the first time.
You fixed the address size check. This has already been fixed in my code. The problem in parsing the info model in the code you proposed remains.
for (; pos != count; ++pos)
{
reply.data.position = 0;
if ((ret = cl_parseNextObject(&reply.data, &obj)) != 0)
{
reply.data.position = reply.data.size;
break;
}
else
{
bb_trim(&reply.data); //In the debug it is clear that we never get here.!!!!!!!!!!!!!!
}
}
This is tested with DLMS meters and it works like expected. cl_parseNextObject fails on the first time because all the data is not received to parse the first object. Amount of access rights is huge.
If you paste the second row in the trace I can check this.
I know the reason. Your meter is using version 0 from Association LN. It returns access_mode as boolean where other versions return enum. Because Association LN is not the first item in the association view we don't know the used version and this fails.
This is now improved and tests are started. New version is released today.
Yes, as I told in the previous post, in full read we know the Association LN version. Now it's unknown because it's not the first item. The first item is the clock.
Hi,
Hi,
I propose that you don't read association view at all. Create objects what you want to read and read only them. Something like this:
gxRegister r;
const unsigned char ln[6] = { 1,1,21,25,0,255 };
INIT_OBJECT(r, DLMS_OBJECT_TYPE_REGISTER, ln);
You need to know the OBIS code and object type.
BR,
Mikko
Reading an information model
Reading an information model is a project requirement. I see the output only in object-by-object parsing of the model with clearing the processed part of the buffer.
Is it possible to change the
Is it possible to change the program to implement the following algorithm: Receive the next portion of useful data (PDU), start the dlms_getValueFromData function, if at least one whole object is received, then we clear the processed useful data buffer and the procedure is repeated.?
Hi,
Hi,
Can you use malloc with your compiler? We have use cl_parseObjectCount and cl_parseNextObject methods when we have use compiler where is no malloc. Get the latest version and try this:
BR,
Mikko
int ret;
message data;
gxReplyData reply;
mes_init(&data);
reply_init(&reply);
unsigned short pos, count;
gxObject obj;
//We are not interested from the access modes.
//If you want to know the access modes allocate enought space for them.
obj.access = NULL;
if ((ret = cl_getObjectsRequest(&connection->settings, &data)) != 0 ||
(ret = com_readDataBlock(connection, &data, &reply)) != 0 ||
(ret = cl_parseObjectCount(&reply.data, &count)) != 0)
{
printf("GetObjects failed %s\r\n", hlp_getErrorMessage(ret));
}
if (ret == 0)
{
for (pos = 0; pos != count; ++pos)
{
if ((ret = cl_parseNextObject(&reply.data, &obj)) != 0)
{
break;
}
//Read object data.
///////////////////////////////////////////////////////////////////////////////////
// Profile generics are read later because they are special cases.
// (There might be so lots of data and we so not want waste time to read all the data.)
if (obj.objectType == DLMS_OBJECT_TYPE_PROFILE_GENERIC)
{
continue;
}
}
}
BR,
Mikko
Hi,
Hi,
This is supported by other programming languages, but we are removing support for this at some point. This is not possible to use when ciphering is used and companies are moving to start use ciphering.
BR,
Mikko
Can you use malloc with your
Can you use malloc with your compiler? - yes. Maybe it makes sense to abandon the dynamic allocation of memory? But then how to properly configure static buffers?
Hi,
Hi,
It's great if you can use malloc because it makes everything easier. If you can't use malloc, you need to use static buffers and it's a pain when you are reading historical (profile generics) or complex data.
Use the code above and start to read the values.
BR,
Mikko
//Read object data.
//Read object data.
/////////////////////////////////////////////////////////////////////////////////// -
what function for this operation?
Your solution allows to parse
Your solution allows to parse objects after receiving a full buffer of useful data (PDU). The my problem is getting the entire amount of data of the info model from the counter in the com_readDataBlock function. Is it possible to parse objects "on the fly" parsing the response from the meter?
Hi,
Hi,
It's not possible to parse objects "on the fly". You need to read all data. Is this problem with historical data (Profile Generics). If it is, you can use readByEntry and readByRange methods to read data from the buffer example day by day.
BR,
Mikko
BR,
Mikko
Good afternoon! My task is to
Good afternoon! My task is to obtain an information model in the form of a table containing only class types and obis codes and store it in the microcontroller's RAM. I do not need to have fully filled objects. Maybe this can be somehow implemented without parsing a completely read buffer, and checking for the presence of an entire object in the buffer, parse it and clear the buffer, read on?
Hi,
Hi,
If you want to only handle association view it might be possible because the structure is not too complicated. Try with this:
int ret;
message data;
gxReplyData reply;
mes_init(&data);
reply_init(&reply);
unsigned short position = 0, pos = 0, count = 0;
gxObject obj;
//We are not interested from the access modes.
//If you want to know the access modes allocate enought space for them.
obj.access = NULL;
if ((ret = cl_getObjectsRequest(&connection->settings, &data)) != 0)
{
printf("GetObjects failed %s\r\n", hlp_getErrorMessage(ret));
}
gxByteBuffer rr;
bb_init(&rr);
//Send data.
if ((ret = readDLMSPacket(connection, data.data[0], &reply)) != DLMS_ERROR_CODE_OK)
{
return ret;
}
//Get object count on the first time.
if (count == 0)
{
if ((ret = cl_parseObjectCount(&reply.data, &count)) != 0)
{
return ret;
}
bb_trim(&reply.data);
}
//Check is there errors or more data from server
do
{
for (; pos != count; ++pos)
{
reply.data.position = 0;
if ((ret = cl_parseNextObject(&reply.data, &obj)) != 0)
{
reply.data.position = reply.data.size;
break;
}
else
{
bb_trim(&reply.data);
}
}
if ((ret = cl_receiverReady(&connection->settings, reply.moreData, &rr)) != DLMS_ERROR_CODE_OK)
{
bb_clear(&rr);
return ret;
}
if ((ret = readDLMSPacket(connection, &rr, &reply)) != DLMS_ERROR_CODE_OK)
{
bb_clear(&rr);
return ret;
}
bb_clear(&rr);
} while (reply_isMoreData(&reply));
for (; pos != count; ++pos)
{
reply.data.position = 0;
if ((ret = cl_parseNextObject(&reply.data, &obj)) != 0)
{
reply.data.position = reply.data.size;
break;
}
else
{
bb_trim(&reply.data);
}
}
mes_clear(&data);
reply_clear(&reply);
BR,
Mikko
for (; pos != count; ++pos)
for (; pos != count; ++pos)
{
reply.data.position = 0;
if ((ret = cl_parseNextObject(&reply.data, &obj)) != 0)
{
reply.data.position = reply.data.size;
break;
}
else
{
// It never goes into this part of the code. And the memory is not freed
bb_trim(&reply.data);
}
}
// It never goes into this
// It never goes into this part of the code. And the memory is not freed
bb_trim(&reply.data);
Hi,
Hi,
What is the object count? Can you post first reply from the meter?
BR,
Mikko
object count is 432!
object count is 432!
Hi,
Hi,
Can you post the first reply from the meter?
BR,
Mikko
first answer from meter is:
first answer from meter is: 7E A0 83 41 02 21 52 1C 14 E6 E7 00 C4 02 81 00 00 00 00 01 00 6C 01 82 01 B0 02 04 12 00 08 11 01 09 06 00 00 01 00 00 FF 02 02 01 09 02 03 0F 01 16 01 00 02 03 0F 02 16 01 00 02 03 0F 03 16 01 00 02 03 0F 04 16 01 00 02 03 0F 05 16 01 00 02 03 0F 06 16 01 00 02 03 0F 07 16 01 00 02 03 0F 08 16 01 00 02 03 0F 09 16 01 00 01 06 02 02 0F 01 03 00 02 02 0F 02 03 00 02 02 0F 03 03 03 02 02 42 7B 7E
from this code: //Send data.
from this code: //Send data.
if ((ret = readDLMSPacket(connection, data.data[0], &reply)) != DLMS_ERROR_CODE_OK)
{
return ret;
}
//Get object count on the first time.
Hi! I think that function "cl
Hi! I think that function "cl_parseNextObject" work not right.
Hi!
Hi!
Maybe you need some more information on the responses from the counter?
Hi,
Hi,
This is fixed. Get the latest version from the source codes. Let me know if you have any problems.
BR,
Mikko
You fixed the address size
You fixed the address size check. This has already been fixed in my code. The problem in parsing the info model in the code you proposed remains.
for (; pos != count; ++pos)
{
reply.data.position = 0;
if ((ret = cl_parseNextObject(&reply.data, &obj)) != 0)
{
reply.data.position = reply.data.size;
break;
}
else
{
bb_trim(&reply.data); //In the debug it is clear that we never get here.!!!!!!!!!!!!!!
}
}
cl_parseNextObject never
cl_parseNextObject never return 0....
And you have not seen a
And you have not seen a program dlms meter emulator?
Hi,
Hi,
This is tested with DLMS meters and it works like expected. cl_parseNextObject fails on the first time because all the data is not received to parse the first object. Amount of access rights is huge.
If you paste the second row in the trace I can check this.
BR,
Mikko
first packet:
first packet:
00> 7E A0 83 41 02 21 52 1C 14 E6
00> E7 00 C4 02 81 00 00 00 00 01
00> 00 6C 01 82 01 B0 02 04 12 00
00> 08 11 01 09 06 00 00 01 00 00
00> FF 02 02 01 09 02 03 0F 01 16
00> 01 00 02 03 0F 02 16 01 00 02
00> 03 0F 03 16 01 00 02 03 0F 04
00> 16 01 00 02 03 0F 05 16 01 00
00> 02 03 0F 06 16 01 00 02 03 0F
00> 07 16 01 00 02 03 0F 08 16 01
00> 00 02 03 0F 09 16 01 00 01 06
00> 02 02 0F 01 03 00 02 02 0F 02
00> 03 00 02 02 0F 03 03 03 02 02
00> 42 7B 7E
parse count 432
cl_parseNextObject = (DLMS_ERROR_CODE)ret DLMS_ERROR_CODE_INVALID_PARAMETER
packet 2:
00> 7E A0 83 41 02 21 74 28 50 E6
00> E7 00 C4 02 81 00 00 00 00 02
00> 00 6C 0F 04 03 00 02 02 0F 05
00> 03 00 02 02 0F 06 03 03 02 04
00> 12 00 0F 11 01 09 06 00 00 28
00> 00 00 FF 02 02 01 08 02 03 0F
00> 01 16 01 00 02 03 0F 02 16 01
00> 01 04 0F 01 0F 02 0F 03 0F 04
00> 02 03 0F 03 16 01 00 02 03 0F
00> 04 16 01 00 02 03 0F 05 16 01
00> 00 02 03 0F 06 16 01 00 02 03
00> 0F 07 16 01 00 02 03 0F 08 16
00> 01 00 01 04 02 02 0F 01 03 00
00> 81 EF 7E
cl_parseNextObject = (DLMS_ERROR_CODE)ret DLMS_ERROR_CODE_INVALID_PARAMETER
packet 3:
00> 7E A0 83 41 02 21 96 34 94 E6
00> E7 00 C4 02 81 00 00 00 00 03
00> 00 6C 02 02 0F 02 03 00 02 02
00> 0F 03 03 00 02 02 0F 04 03 00
00> 02 04 12 00 0F 11 01 09 06 00
00> 00 28 00 01 FF 02 02 01 08 02
00> 03 0F 01 16 01 00 02 03 0F 02
00> 16 01 01 04 0F 01 0F 02 0F 03
00> 0F 04 02 03 0F 03 16 01 00 02
00> 03 0F 04 16 01 00 02 03 0F 05
00> 16 01 00 02 03 0F 06 16 01 00
00> 02 03 0F 07 16 00 00 02 03 0F
00> 08 16 01 00 01 04 02 02 0F 01
00> CA FE 7E
cl_parseNextObject = (DLMS_ERROR_CODE)ret DLMS_ERROR_CODE_INVALID_PARAMETER
packet4:
00> 7E A0 83 41 02 21 B8 48 5C E6
00> E7 00 C4 02 81 00 00 00 00 04
00> 00 6C 03 00 02 02 0F 02 03 00
00> 02 02 0F 03 03 00 02 02 0F 04
00> 03 00 02 04 12 00 0F 11 01 09
00> 06 00 00 28 00 02 FF 02 02 01
00> 08 02 03 0F 01 16 01 00 02 03
00> 0F 02 16 01 01 04 0F 01 0F 02
00> 0F 03 0F 04 02 03 0F 03 16 01
00> 00 02 03 0F 04 16 01 00 02 03
00> 0F 05 16 01 00 02 03 0F 06 16
00> 01 00 02 03 0F 07 16 00 00 02
00> 03 0F 08 16 00 00 01 04 02 02
00> B9 1C 7E
cl_parseNextObject = (DLMS_ERROR_CODE)ret DLMS_ERROR_CODE_INVALID_PARAMETER
packet 5:
00> 7E A0 83 41 02 21 DA 5C 1C E6
00> E7 00 C4 02 81 00 00 00 00 05
00> 00 6C 0F 01 03 00 02 02 0F 02
00> 03 00 02 02 0F 03 03 00 02 02
00> 0F 04 03 00 02 04 12 00 17 11
00> 01 09 06 00 00 16 00 00 FF 02
00> 02 01 09 02 03 0F 01 16 01 00
00> 02 03 0F 02 16 01 00 02 03 0F
00> 03 16 01 00 02 03 0F 04 16 01
00> 00 02 03 0F 05 16 01 00 02 03
00> 0F 06 16 01 00 02 03 0F 07 16
00> 01 00 02 03 0F 08 16 01 00 02
00> 03 0F 09 16 01 00 01 00 02 04
00> 8C 3D 7E
cl_parseNextObject = (DLMS_ERROR_CODE)ret DLMS_ERROR_CODE_INVALID_PARAMETER
packet 6:
00> 7E A0 83 41 02 21 FC 68 58 E6
00> E7 00 C4 02 81 00 00 00 00 06
00> 00 6C 12 00 13 11 01 09 06 00
00> 00 14 00 00 FF 02 02 01 09 02
00> 03 0F 01 16 01 00 02 03 0F 02
00> 16 01 00 02 03 0F 03 16 01 00
00> 02 03 0F 04 16 01 00 02 03 0F
00> 05 16 01 00 02 03 0F 06 16 00
00> 00 02 03 0F 07 16 00 00 02 03
00> 0F 08 16 00 00 02 03 0F 09 16
00> 00 00 01 00 02 04 12 00 07 11
00> 01 09 06 00 00 5E 07 00 FF 02
00> 02 01 08 02 03 0F 01 16 01 00
00> 55 C2 7E
cl_parseNextObject = (DLMS_ERROR_CODE)ret DLMS_ERROR_CODE_INVALID_PARAMETER
more packets?
more packets?
Hi,
Hi,
I know the reason. Your meter is using version 0 from Association LN. It returns access_mode as boolean where other versions return enum. Because Association LN is not the first item in the association view we don't know the used version and this fails.
This is now improved and tests are started. New version is released today.
BR,
Mikko
However, with a full poll of
However, with a full poll of the info model, the com_getAssociationView function worked!
Hi,
Hi,
Yes, as I told in the previous post, in full read we know the Association LN version. Now it's unknown because it's not the first item. The first item is the clock.
BR,
Mikko
Good! How soon can I expect
Good! How soon can I expect fix?
Hi,
Hi,
The new version is released just a few minutes ago.
BR,
Mikko
Cool! Thank you for
Cool! Thank you for efficiency!
Everything works, thanks for
Everything works, thanks for the help! The topic can be closed.