Second, we downloaded the latest release and we are starting the testing so we opened this discussion to report the bugs.
When we open the new project and build it, there is some errors mainly do to some missing casting (check the picture).
The issue is that in the file notify.c in the notify_generatePushSetupMessages() function, you replaced "gxByteBuffer* pdu" by "gxByteBuffer pdu". But you didn't change "pdu = settings->serializedPdu" knowing that the dlmsSettings structure contains "gxByteBuffer* serializedPdu".
In the same function you used the function notify_addData() that takes "gxByteBuffer* buff" as the 4 elements. But you did pass the variable pdu that it is not a pointer.
Let me highlight another problem that we expected to be solved.
The 2 objects of type gxData with value type equal to array[2] of bit-string[256] and array[2] of octet-string[12] respectively are causing some problems.
In fact, before this release: we were able to save these objects and then read them normally on the client side but we were not able to load them. So when we try to restart the meter it was giving "Failed to load settings!".
Now with this release : we are not even able to read these objects, the director is giving invalid data type (check the picture). And when we try to restart the meter it is giving "Assertion failed: 0".
If you please can check it and solve the issues around these 2 value types. And inform us if you have any idea why this is happening.
We noticed that in the latest release you made some change linked to the activity calendar object. But we realized that the method "Activate passive calendar", that should copy all passive calendar targets to active, is not implemented yet in the gxinvoke.c file. However on the client side we have the button "Activate" that should trigger this action but on the server side it is missing, if you please can implement this feature.
Serialization has changed. You need to modify the getProfileGenericBufferColumnSizes, captureProfileGeneric, and readProfileGeneric. There are two reasons for this. Now serialization is using less memory and values are always saved using MSB byte order.
Check the changes and let me know if you have problems.
We downloaded the latest version and we are testing it right now. We noticed all the improvements and optimization made to the serialization.
But till now the above issue is still here. When we create an object gxData of value type array[2] of bit-string[256] or array[2] of octet-string[12], it is still unreadable by the director (invalid data type) and when trying to load the meter it is giving "assertion failed"
If you please can check it and maybe add these 2 objects to the next release.
Best Regards,
Lara Wakim
PS: you can find below the 2 methods to add these 2 objects. This will also help you to repeat more easily the problem.
int addEventLogFilter_standardEventLog()
{
int ret;
const unsigned char ln[6] = { 0, 1, 94, 34, 105, 255 };
if ((ret = INIT_OBJECT(eventLogFilter_standardEventLog, DLMS_OBJECT_TYPE_DATA, ln)) == 0)
{
static char BUFFER[2][32]; //32*8= 256
for (int i = 0; i < 32; i++)
{
//Array[0]=default values: 1 for all bits (all events will be log into the buffer)
BUFFER[0][i] = 0xff;
//Array[1]=default values: 0 for all bit (no event will be sent as notification event)
BUFFER[1][i] = 0;
}
static dlmsVARIANT ARRAY[2];
GX_BIT_STRING(ARRAY[0], BUFFER[0], 256);
GX_BIT_STRING(ARRAY[1], BUFFER[1], 256);
static variantArray tmp;
VA_ATTACH(tmp, ARRAY, 2);
GX_ARRAY(eventLogFilter_standardEventLog.value, tmp);
}
return ret;
}
int addTimestampForNewCalendarActivation()
{
int ret;
const unsigned char ln[6] = { 1, 0, 94, 34, 130, 255 };
if ((ret = INIT_OBJECT(timestampForNewCalendarActivation, DLMS_OBJECT_TYPE_DATA, ln)) == 0)
{
//Array[3] of octet-string[12]
//Array[x] Date&Time for contract x (x =1..3)
Let me share with you some comments from our side concerning the new release and especially the new implemented function svr_handleActivityCalendar():
- First we noticed that concerning the season profile you are comparing the start time of the season with the current time through "time_compare2(&tm, time)" to test it we created 2 seasons winter and summer:
The goal is to compare current time with the start time of each season and to check in which interval we are. In our case (12/16) we are in the "winter time" but when looping through seasonProfileActive.size the time_compare2(&tm, time) is also different from 0 which is wrong because we are in the winter season. We think we should not check if the day and moth are equal but we should check in which interval our current date is included.
To be able to continue with the testing we replaced this line:
time_init(&ACTIVE_SEASON_PROFILE[1].start, -1, 10, 30, -1, -1, -1, -1, 0x8000);
with this one:
time_init(&ACTIVE_SEASON_PROFILE[1].start, 2020, 12, 16, -1, -1, -1, -1, 0x8000);
When replaced the time_compare2(&tm, time) gave 0 and we were able to continue the testing.
- Concerning the week profile it is working like expected, it checked first which weekName was selected with the chosen season and then we saved the dayId corresponding for the day of the week (Wednesday in our case). The output of this step is dayId = 1 (check below)
- Concerning the day profile, it is implemented in a different way from our requirements. If you check the attached picture the day profile should be implemented in this way:
Start_time: is a TIME(OCTET-STRING[4]) where seconds and hundredths are not configurable (value0x00).
script_logical_name: octet-string[6], is a dummy script.
Script_selector: Less significant byte identifies directly the rate.
Example for less significant Byte: (0x01 means rate1, 0x02 means rate2, 0x03 means rate3,…0x04 means rate 4)
This means that the script is null and the script selector is used to identify which is the current tariff.
In fact, in our examlple we are interested with the dayId = 1 implemented like this:
So first we have to check and compare our time with the different start time which IS NOT implemented in your code AND SHOULD BE PRESENT. After comparing and identifying in which interval we are (our case around 2:30 pm so third case between 12:00 and 23:00) so the scriptSelector = 1 so the current tariff is equal to 1. This is what we need in our case.
Surely the svr_invoke() function is needed in case the script is different from Null and should be triggered on time.
- Finally there is a last feature that is not taken in consideration yet. In fact before checking all this step starting with the season, we should check if the current date is included in the special day table. If not, we follow the step described above. If yes, we jump directly to the day profile table because with each special day we have its own dayId leading us to the current tariff.
Sorry for this long text and explanation.
Hope you will be able to add the missing piece and correct the bugs. If you need more information or you need any help feel free to ask.
After checking the blue book, we noticed that you followed the attached scheme (check the picture) and this is exactly what we want and the implementation that you did is almost done and correct. We noticed that you already created the tariffication script table and the register activation.
To sum up what is missing in the activity calendar:
1- To check the special day table before checking the season table
2 - When checking the season table, we should only see in which interval season our current date is included (not compare equally)
3 - When checking the day table, we should also see in which interval of time our current time (hours and minutes) is included and then we can trigger the tariffication script with the expected script selector.
We were updating the tarrification script table to match our requirements and surprisingly when trying to run it: in the gxserializer.c file -> in the ser_savescripttable() function -> the "(ret = arr_getByIndex(&s->actions, pos2, (void**)&sa, sizeof(gxScriptAction))) != 0" was throwing an exception read access violation (check the picture).
To easily repeat the exception simply replace the addTarifficationScriptTable() by the following, you should have the same problem (if you also please check if we didn't write something wrong):
Season profile:
You are right. It's only executed when the new season starts. We have modified this and are testing so it's executed starting from that date and not just on the starting moment.
Day profile:
Start_time: Use zero for second and ms, not -1.
time_init(&ACTIVE_DAY_PROFILE_ACTIONS1[0].startTime, -1, -1, -1, 1, 0, 0, 0, 0x8000);
We can implement that if the script is NULL (not defined), there is no script to invoke and svr_invoke is called with the activity calendar as a parameter.
Jumping directly to the day profile and use dayID is not possible. Starting day must define in the season profile. Implementing it like this will break the DLMS standard.
1 - Concerning the start time of the day profile, you are right.
But don't forget that you should check in which time interval (day schedule) we are currently before triggering the tariffication script exactly like you did with the season profile.
2 - We are not interested anymore with the NULL script. We want to trigger the tariffication script already implemented like suggested in the DLMS standard.
3 - You are right.
But also don't forget that when reaching the day profile we have to check if this day is one of the special day (in the special day table object):
If no we continue the testing normally by checking the start time then triggering the tariffication script
If yes we should use the dayId table noted with the corresponding special day then checking the start time then triggering the tariffication script
If you have any comment or need more information feel free to ask.
Support for special days is added and it's released on Monday.
There is a new version where activity calendar support is improved. There are also changes for the example server code.
Hi,
Hi,
Let me highlight another problem that we expected to be solved.
The 2 objects of type gxData with value type equal to array[2] of bit-string[256] and array[2] of octet-string[12] respectively are causing some problems.
In fact, before this release: we were able to save these objects and then read them normally on the client side but we were not able to load them. So when we try to restart the meter it was giving "Failed to load settings!".
Now with this release : we are not even able to read these objects, the director is giving invalid data type (check the picture). And when we try to restart the meter it is giving "Assertion failed: 0".
If you please can check it and solve the issues around these 2 value types. And inform us if you have any idea why this is happening.
Best Regards,
Lara Wakim
Hi Mikko,
Hi Mikko,
We noticed that in the latest release you made some change linked to the activity calendar object. But we realized that the method "Activate passive calendar", that should copy all passive calendar targets to active, is not implemented yet in the gxinvoke.c file. However on the client side we have the button "Activate" that should trigger this action but on the server side it is missing, if you please can implement this feature.
Best Regards,
Lara Wakim
Hi,
Hi,
Serialization has changed. You need to modify the getProfileGenericBufferColumnSizes, captureProfileGeneric, and readProfileGeneric. There are two reasons for this. Now serialization is using less memory and values are always saved using MSB byte order.
Check the changes and let me know if you have problems.
BR,
Mikko
Hi,
Hi,
This is released today. Tests were ongoing yesterday and for this reason, it was not released. Get the latest version.
BR,
Mikko
Hi,
Hi,
We downloaded the latest version and we are testing it right now. We noticed all the improvements and optimization made to the serialization.
But till now the above issue is still here. When we create an object gxData of value type array[2] of bit-string[256] or array[2] of octet-string[12], it is still unreadable by the director (invalid data type) and when trying to load the meter it is giving "assertion failed"
If you please can check it and maybe add these 2 objects to the next release.
Best Regards,
Lara Wakim
PS: you can find below the 2 methods to add these 2 objects. This will also help you to repeat more easily the problem.
int addEventLogFilter_standardEventLog()
{
int ret;
const unsigned char ln[6] = { 0, 1, 94, 34, 105, 255 };
if ((ret = INIT_OBJECT(eventLogFilter_standardEventLog, DLMS_OBJECT_TYPE_DATA, ln)) == 0)
{
static char BUFFER[2][32]; //32*8= 256
for (int i = 0; i < 32; i++)
{
//Array[0]=default values: 1 for all bits (all events will be log into the buffer)
BUFFER[0][i] = 0xff;
//Array[1]=default values: 0 for all bit (no event will be sent as notification event)
BUFFER[1][i] = 0;
}
static dlmsVARIANT ARRAY[2];
GX_BIT_STRING(ARRAY[0], BUFFER[0], 256);
GX_BIT_STRING(ARRAY[1], BUFFER[1], 256);
static variantArray tmp;
VA_ATTACH(tmp, ARRAY, 2);
GX_ARRAY(eventLogFilter_standardEventLog.value, tmp);
}
return ret;
}
int addTimestampForNewCalendarActivation()
{
int ret;
const unsigned char ln[6] = { 1, 0, 94, 34, 130, 255 };
if ((ret = INIT_OBJECT(timestampForNewCalendarActivation, DLMS_OBJECT_TYPE_DATA, ln)) == 0)
{
//Array[3] of octet-string[12]
//Array[x] Date&Time for contract x (x =1..3)
static unsigned char BUFFER[3][12];
static dlmsVARIANT ARRAY[3];
GX_OCTECT_STRING(ARRAY[0], BUFFER[0], sizeof(BUFFER[0]));
GX_OCTECT_STRING(ARRAY[1], BUFFER[1], sizeof(BUFFER[1]));
GX_OCTECT_STRING(ARRAY[2], BUFFER[2], sizeof(BUFFER[2]));
static variantArray tmp;
VA_ATTACH(tmp, ARRAY, 3);
GX_ARRAY(timestampForNewCalendarActivation.value, tmp);
}
return ret;
}
Hi Mikko,
Hi Mikko,
Let me share with you some comments from our side concerning the new release and especially the new implemented function svr_handleActivityCalendar():
- First we noticed that concerning the season profile you are comparing the start time of the season with the current time through "time_compare2(&tm, time)" to test it we created 2 seasons winter and summer:
//Add active season profile.
ARR_ATTACH(activityCalendar.seasonProfileActive, ACTIVE_SEASON_PROFILE, 2);
// add Summer season
SET_OCTET_STRING(ACTIVE_SEASON_PROFILE[0].name, "Summer time", 11);
SET_OCTET_STRING(ACTIVE_SEASON_PROFILE[0].weekName, "Summer week", 11);
time_init(&ACTIVE_SEASON_PROFILE[0].start, -1, 3, 31, -1, -1, -1, -1, 0x8000);
//add winter season
SET_OCTET_STRING(ACTIVE_SEASON_PROFILE[1].name, "Winter time", 11);
SET_OCTET_STRING(ACTIVE_SEASON_PROFILE[1].weekName, "Winter week", 11 );
time_init(&ACTIVE_SEASON_PROFILE[1].start, -1, 10, 30, -1, -1, -1, -1, 0x8000);
The goal is to compare current time with the start time of each season and to check in which interval we are. In our case (12/16) we are in the "winter time" but when looping through seasonProfileActive.size the time_compare2(&tm, time) is also different from 0 which is wrong because we are in the winter season. We think we should not check if the day and moth are equal but we should check in which interval our current date is included.
To be able to continue with the testing we replaced this line:
time_init(&ACTIVE_SEASON_PROFILE[1].start, -1, 10, 30, -1, -1, -1, -1, 0x8000);
with this one:
time_init(&ACTIVE_SEASON_PROFILE[1].start, 2020, 12, 16, -1, -1, -1, -1, 0x8000);
When replaced the time_compare2(&tm, time) gave 0 and we were able to continue the testing.
- Concerning the week profile it is working like expected, it checked first which weekName was selected with the chosen season and then we saved the dayId corresponding for the day of the week (Wednesday in our case). The output of this step is dayId = 1 (check below)
SET_OCTET_STRING(ACTIVE_WEEK_PROFILE[0].name, "Summer week", 11 );
wp = &ACTIVE_WEEK_PROFILE[0];
wp->monday = wp->tuesday = wp->wednesday = wp->thursday = wp->friday = 2;
wp->saturday = wp->sunday = 3;
SET_OCTET_STRING(ACTIVE_WEEK_PROFILE[0].name, "Winter week", 11 );
wp = &ACTIVE_WEEK_PROFILE[1];
wp->monday = wp->tuesday = wp->wednesday = wp->thursday = wp->friday = 1;
wp->saturday = wp->sunday = 3;
- Concerning the day profile, it is implemented in a different way from our requirements. If you check the attached picture the day profile should be implemented in this way:
Start_time: is a TIME(OCTET-STRING[4]) where seconds and hundredths are not configurable (value0x00).
script_logical_name: octet-string[6], is a dummy script.
Script_selector: Less significant byte identifies directly the rate.
Example for less significant Byte: (0x01 means rate1, 0x02 means rate2, 0x03 means rate3,…0x04 means rate 4)
This means that the script is null and the script selector is used to identify which is the current tariff.
In fact, in our examlple we are interested with the dayId = 1 implemented like this:
ARR_ATTACH(activityCalendar.dayProfileTableActive, ACTIVE_DAY_PROFILE, 3);
ACTIVE_DAY_PROFILE[0].dayId = 1;
ARR_ATTACH(ACTIVE_DAY_PROFILE[0].daySchedules, ACTIVE_DAY_PROFILE_ACTIONS1, 4);
time_init(&ACTIVE_DAY_PROFILE_ACTIONS1[0].startTime, -1, -1, -1, 1, 0, -1, -1, 0x8000);
ACTIVE_DAY_PROFILE_ACTIONS1[0].scriptSelector = 1;
time_init(&ACTIVE_DAY_PROFILE_ACTIONS1[1].startTime, -1, -1, -1, 11, 0, -1, -1, 0x8000);
ACTIVE_DAY_PROFILE_ACTIONS1[1].scriptSelector = 2;
time_init(&ACTIVE_DAY_PROFILE_ACTIONS1[2].startTime, -1, -1, -1, 12, 0, -1, -1, 0x8000);
ACTIVE_DAY_PROFILE_ACTIONS1[2].scriptSelector = 1;
time_init(&ACTIVE_DAY_PROFILE_ACTIONS1[3].startTime, -1, -1, -1, 23, 0, -1, -1, 0x8000);
ACTIVE_DAY_PROFILE_ACTIONS1[3].scriptSelector = 2;
So first we have to check and compare our time with the different start time which IS NOT implemented in your code AND SHOULD BE PRESENT. After comparing and identifying in which interval we are (our case around 2:30 pm so third case between 12:00 and 23:00) so the scriptSelector = 1 so the current tariff is equal to 1. This is what we need in our case.
Surely the svr_invoke() function is needed in case the script is different from Null and should be triggered on time.
- Finally there is a last feature that is not taken in consideration yet. In fact before checking all this step starting with the season, we should check if the current date is included in the special day table. If not, we follow the step described above. If yes, we jump directly to the day profile table because with each special day we have its own dayId leading us to the current tariff.
Sorry for this long text and explanation.
Hope you will be able to add the missing piece and correct the bugs. If you need more information or you need any help feel free to ask.
Best regards,
Lara Wakim
Hi Mikko,
Hi Mikko,
We are sorry for the above long explanation.
After checking the blue book, we noticed that you followed the attached scheme (check the picture) and this is exactly what we want and the implementation that you did is almost done and correct. We noticed that you already created the tariffication script table and the register activation.
To sum up what is missing in the activity calendar:
1- To check the special day table before checking the season table
2 - When checking the season table, we should only see in which interval season our current date is included (not compare equally)
3 - When checking the day table, we should also see in which interval of time our current time (hours and minutes) is included and then we can trigger the tariffication script with the expected script selector.
We are very grateful for your hard work.
Best Regards,
Lara Wakim
Hello Mikko,
Hello Mikko,
We were updating the tarrification script table to match our requirements and surprisingly when trying to run it: in the gxserializer.c file -> in the ser_savescripttable() function -> the "(ret = arr_getByIndex(&s->actions, pos2, (void**)&sa, sizeof(gxScriptAction))) != 0" was throwing an exception read access violation (check the picture).
To easily repeat the exception simply replace the addTarifficationScriptTable() by the following, you should have the same problem (if you also please check if we didn't write something wrong):
int addTarifficationScriptTable()
{
int ret;
static gxScript SCRIPTS[4] = { 0 };
static gxScriptAction ACTIONS1[1] = { 0 };
static gxScriptAction ACTIONS2[1] = { 0 };
static gxScriptAction ACTIONS3[1] = { 0 };
static gxScriptAction ACTIONS4[1] = { 0 };
const unsigned char ln[6] = { 0, 0, 10, 0, 100, 255 };
if ((ret = INIT_OBJECT(tarifficationScriptTable, DLMS_OBJECT_TYPE_SCRIPT_TABLE, ln)) == 0)
{
SCRIPTS[0].id = 1; //rate 1
SCRIPTS[1].id = 2; //rate 2
SCRIPTS[2].id = 3; //rate 3
SCRIPTS[3].id = 4; //rate 4
ARR_ATTACH(tarifficationScriptTable.scripts, SCRIPTS, 4);
ARR_ATTACH(SCRIPTS[0].actions, ACTIONS1, 1);
ACTIONS1[0].type = DLMS_SCRIPT_ACTION_TYPE_WRITE;
ACTIONS1[0].target = BASE(registerActivation);
ACTIONS1[0].index = 4;
var_init(&ACTIONS1[0].parameter);
//Action data is Int8 zero.
GX_INT8(ACTIONS1[0].parameter) = 3;
ARR_ATTACH(SCRIPTS[1].actions, ACTIONS2, 1);
ACTIONS2[0].type = DLMS_SCRIPT_ACTION_TYPE_WRITE;
ACTIONS2[0].target = BASE(registerActivation);
ACTIONS2[0].index = 4;
var_init(&ACTIONS2[0].parameter);
//Action data is Int8 zero.
GX_INT8(ACTIONS2[0].parameter) = 0;
ARR_ATTACH(SCRIPTS[2].actions, ACTIONS3, 1);
ACTIONS3[0].type = DLMS_SCRIPT_ACTION_TYPE_WRITE;
ACTIONS3[0].target = BASE(registerActivation);
ACTIONS3[0].index = 4;
var_init(&ACTIONS3[0].parameter);
//Action data is Int8 zero.
GX_INT8(ACTIONS3[0].parameter) = 0;
ARR_ATTACH(SCRIPTS[3].actions, ACTIONS4, 1);
ACTIONS4[0].type = DLMS_SCRIPT_ACTION_TYPE_WRITE;
ACTIONS4[0].target = BASE(registerActivation);
ACTIONS4[0].index = 4;
var_init(&ACTIONS4[0].parameter);
//Action data is Int8 zero.
GX_INT8(ACTIONS4[0].parameter) = 0;
}
return ret;
}
If you need more information feel free to ask or if you have any idea why this is happening please share it.
Best Regards,
Lara Wakim
Hi,
Hi,
There was an issue with serialization. That is now fixed. Get the latest version.
BR,
Mikko
Hi,
Hi,
Season profile:
You are right. It's only executed when the new season starts. We have modified this and are testing so it's executed starting from that date and not just on the starting moment.
Day profile:
Start_time: Use zero for second and ms, not -1.
time_init(&ACTIVE_DAY_PROFILE_ACTIONS1[0].startTime, -1, -1, -1, 1, 0, 0, 0, 0x8000);
We can implement that if the script is NULL (not defined), there is no script to invoke and svr_invoke is called with the activity calendar as a parameter.
Jumping directly to the day profile and use dayID is not possible. Starting day must define in the season profile. Implementing it like this will break the DLMS standard.
BR,
Mikko
Hi Mikko,
Hi Mikko,
Concerning the day profile:
1 - Concerning the start time of the day profile, you are right.
But don't forget that you should check in which time interval (day schedule) we are currently before triggering the tariffication script exactly like you did with the season profile.
2 - We are not interested anymore with the NULL script. We want to trigger the tariffication script already implemented like suggested in the DLMS standard.
3 - You are right.
But also don't forget that when reaching the day profile we have to check if this day is one of the special day (in the special day table object):
If no we continue the testing normally by checking the start time then triggering the tariffication script
If yes we should use the dayId table noted with the corresponding special day then checking the start time then triggering the tariffication script
If you have any comment or need more information feel free to ask.
Best Regards,
Lara Wakim
Hi Lara,
Hi Lara,
Support for special days is added and it's released on Monday.
There is a new version where activity calendar support is improved. There are also changes for the example server code.
BR,
Mikko