Embedded software compatibility considerations

Edited from: https://mp.weixin.qq.com/s/sxiyEs7rFBJgNXdzMWAoSA

There are many jokes about code on the Internet, such as: If you want not to be replaced by others, the more messy the code, the better.

When the version is iterated, the person who handles this project will usually crash. If you have not left your job at this time, you will also crash, because you may no longer know your own code.

In the overall design stage of the project system, the situation that can be encountered in the future should be considered as much as possible to cover as many business expansions as possible. Although the project is developed in stages, the functions completed in each stage are different, and the overall design should point to the final requirements.

1. Formulation of agreements

The established protocol should satisfy the interaction of all data in the entire project.

for example:

picture

The ID here is set to 1 byte, which may have certain risks. The following functions are added, and the ID of 1 byte may not be satisfied, so the protocol has to be changed. Although it may meet the needs of a certain project, in case other projects use this set of code later, but the 1-byte ID cannot meet the requirements, the code has to be changed. It may be better to set 2 bytes here, which can basically meet the use of most situations.

It is not necessary to set 4 bytes for wider coverage, which may be a bit redundant. In many situations, there is a balance point that needs to be weighed by yourself.

The Length field is only set to 1 byte, which may also have some dedication. If there is a large amount of data to be sent in the later functions, it may be divided into many packages and sent. It could have been sent quickly, but it is restricted here. At that time, if you want to speed up, you have to change the agreement.

In the previous project, the communication between several boards used the same protocol. However, it was later discovered that the agreement could not meet the new needs, but in order not to affect the previous data, another set of agreements was established. As a result, there are two sets of almost identical protocol processing codes in the project.

The agreement should be considered and formulated at the beginning of the project, and should not be changed during the entire project development period.

2. Data addition

The new data added later should not be inserted into the existing data, but a data ID should be added separately.

for example:

Among the existing data, there is a piece of data called device information, which includes: device IP, device Mac. This data will be displayed on the mobile APP. Corresponding C language code:

#define  MSG_ID_DEV_INFO   0x0001

typedef struct _dev_info
{
 char dev_ip[IP_MAX_LEN];
 char dev_mac[MAC_MAX_LEN];
}dev_info_t;

If we need to display the sn of another device later, where should we add this data?

If it is in the early and mid-term of the project, it is still in the development stage at this time, and we can modify it at will. Because the device sn is also part of the device information, it is more reasonable to add it directly to the data of the device information:

#define  MSG_ID_DEV_INFO   0x0001

typedef struct _dev_info
{
 char dev_ip[IP_MAX_LEN];
 char dev_mac[MAC_MAX_LEN];
    char dev_sn[SN_MAX_LEN];
}dev_info_t;

If the product is already in circulation in the market, if this is added at this time, the software compatibility will not be very good. Because if the version of your mobile phone APP does not match the version of the device, the original device IP and device MAC may not be displayed, because we have changed the original data structure, and the mobile phone APP according to The original data is used for analysis, but it cannot be analyzed.

At this time, you can add it like this:

#define  MSG_ID_DEV_INFO   0x0001
#define  MSG_ID_DEV_SN     0x0002

typedef struct _dev_info
{
 char dev_ip[IP_MAX_LEN];
 char dev_mac[MAC_MAX_LEN];
}dev_info_t;

typedef struct _dev_sn
{
    char dev_sn[SN_MAX_LEN];
}dev_sn_t;

In this way, even if the mobile APP version does not correspond to the device version, the original data can still be displayed normally. Of course, the best situation is of course that it is reasonably designed in the development stage. Otherwise, in this case, you can only sacrifice some program readability in exchange for program compatibility. This will make people who read the code feel very strange. Isn’t your SN also data device information, why do you give a separate ID. The person who takes over this code later may change this piece of code.

picture

Reminder: If you don’t fully understand why the code of the maintenance project is implemented in this way, it’s better not to mess with the program that can run normally, even if you think it’s shitty code. Otherwise there could be big problems. Either wait until the software is refactored to make changes, or keep patching.

3. Data deletion

If you want to delete the data you use inside this module, you can delete it however you want.

If it is to delete data that interacts with other modules, this cannot be so arbitrary. For example, the way of request and response. The data returned by the responding end to the requesting end cannot be deleted arbitrarily. If you want to delete it, you must ensure that the requesting end has no need to request data and the relevant code has been removed. Then the responding end will delete the data, otherwise it will still be kept. Bar.

4. Data modification

In fact, the data is fixed. In order to ensure software compatibility, modification should be prohibited.

If you must modify it, you can add a new piece of data first, and then slowly cut into new data and delete old data.

The interface being used should not be modified as much as possible. If you want to modify it, you must not affect the previous function.

I have encountered similar situations before. for example:

typedef enum _sys_status
{
 SYS_STATUS_IDLE,
 SYS_STATUS_RUNNING,
 SYS_STATUS_STOP,
}sys_status_t;

static sys_status_t g_sys_status;

sys_status_t get_sys_status(void)
{
 return g_sys_status;
}

The system status here is to be displayed on the mobile APP, and different statuses display different icons. A new state will be added later, and the resulting data provider will be changed as follows:

typedef enum _sys_status
{
 SYS_STATUS_IDLE,
 SYS_STATUS_NEW_STATUS,
 SYS_STATUS_RUNNING,
 SYS_STATUS_STOP,
}sys_status_t;

static sys_status_t g_sys_status;

sys_status_t get_sys_status(void)
{
 return g_sys_status;
}

The interface provider inserts the new data into the middle, which affects the original order of the enumeration. As a result, the icon display on the mobile app was messed up.

The modification that affects the interface must ensure that the original data is not affected.

During the software upgrade process, it is necessary to consider whether other system components on which the software depends have changed, so as to ensure that the software can run normally after the upgrade without affecting the normal operation of other system components. If other system components change, relevant testing and documentation updates are required to ensure that the overall system functions properly.

In addition, with the iteration of the project, this set of code may run on different chip platforms of different systems.

For example, in our embedded Linux project, some third-party libraries will be used in some projects, which may be compiled into the form of dynamic libraries at this time. It is necessary to consider whether the dynamic library can be upgraded when upgrading. If not, you have to compile the dependent libraries together into the executable program.

In addition, if a dynamic library is used, and the software and hardware of the product are iteratively changed to a chip platform, we need to cross-compile the dependent library again. If you want to ensure the compatibility of this piece, you can also consider compiling the dependent library together with the user code.

Of course, this is a trade-off based on the actual situation. If there are many dependent libraries, they are compiled into the executable program together, resulting in a large executable program, and it is not easy to update it when the time comes.

When it comes to the addition of functions, try not to affect the previously developed functions. Otherwise, it will also increase the user's learning cost for the product. For example, what does the fast flashing and slow flashing of some indicators in the previous version mean? Try not to modify this in the future, otherwise users will have to understand it again.

During the software upgrade process, it is necessary to consider whether the performance of the software changes, so as to ensure that the performance of the software after the upgrade can still meet the needs of users. If the performance of the software changes, relevant tests and optimizations need to be carried out to ensure that the software can run normally and meet the performance requirements of users.

For example, it used to take 3 minutes to upgrade normally, but after a certain version, the upgrade became 6 minutes, and the upgrade efficiency became worse.

During the software upgrade process, it is necessary to consider whether the security of the software has been enhanced to ensure that the security of the upgraded software can be guaranteed and no new security risks will arise. If the security of the software is hardened, relevant testing and documentation updates are required to ensure that the software works properly and keeps users safe.

Relevant information:

https://blog.csdn.net/zhang_yin_liang/article/details/129469171

Guess you like

Origin blog.csdn.net/qq_41854911/article/details/131525047