New Object Dictionary

Object dictionary is the oldest part of this project. It is time, to make some improvements. I was thinking about multiple different principles.
I think, that general principle of the current implementation is quite good. But implementation could be much better.
Here are my ideas:

GUI tool libedssharp will be used for generating CO_OD files. Object dictionary can be generated directly from standard xdd (or EDS) file, no specific options or special project file needed. (I hope so).
Remove RAM, ROM, EEPROM. Objects are initialized globally (as now), but each index separately. No need for defines in CO_OD.h. Storing of the variables should be handled by application.
Structure definitions as specified in CANopen, for example, OD entry 0x0020 - DEFSTRUCT - PDO_COMMUNICATION_PARAMETER, etc. Not for each object of type RECORD separately.
I would like implementation of some kind of grouping in libedssharp (modules). I would like to get for example:

typedef struct {
  OD_RPDOCommunicationParameter_t communicationParameter;
  OD_RPDOMappingParameter_t mappingParameter;
} OD_rpdo_t
OD_rpdo_t OD_rpdo[NO_RPDO] = {... initial values ...};

Object dictionary array (CO_OD[]) is collection of objects, one entry for each index implemented, as it is now. But it should not be const, but configurable by application via function calls. No need for extra CO_OD_extension_t.
SDO callback function for OD variable reviewed, should be also used for PDO.
Application may have some parameters attached to specific OD variable: It can verify, if variable is mapped into PDO, in that case it can request PDO send or check, if PDO was transmitted. No ChangeOfState flag in Object Dictionary Editor, no CO_TPDOisCOS() function.
uint16_t instead of UNSIGNED16, etc. usage in CO_OD.c.h

Approach from my previous post is currently used for CANopen objects. But I'm clearing my idea now.
Implement getters/setters in two directions use from application or use from SDO, PDO, storage.
getters/setters for usage from application
Something as recommended by @martinwag, for example:
uint16_t OD_readUint16(CO_OD_entry_t *entry, uint8_t subIdx, returnError_t *err);
Function would verify type and return data from OD_entry. Call like this:
uint16_t myvar = OD_readUint16(OD_find(SDO, 0x1017), 0x00, &err);
Or optimized, if libedssharp will define correct CO_OD_INDEX_1017.
uint16_t myvar = OD_readUint16(CO_OD[CO_OD_INDEX_1017], subIdx, &err);
This could also be used inside initialization of CANopen objects for fetching default values. So there would be no need for mess with two types  OD_TPDOCommunicationParameter_t and CO_TPDOCommPar_t as in the comment above and call to CO_TPDO_init() would be much simpler.
getters/setters for usage from SDO, PDO or storage:
This is advanced functionality. CANopen object or user object can change specific CO_OD_entry_t *entry - register own getters/setters for data. Then data won't be read from structure defined by libedssharp anymore. For example, when SDO server will try to read data value from OD, it will call getter, which will calculate the data value from internal state of the specified object.

Something I've used before for a similar situation my apply here for access to the OD from the application side:
Define a struct of functions pointers that mean all access to all OD entries are via a function (pointer) call. The advantages of this approach are:
So you end up with something like: (pseudocode)
struct
{
   uint16_t(*countslave_tiltdata_get);
   void(*countslave_tiltdata_set)(uint16_t);
   ...
} OD;

In my example countslave_tiltdata comes from the EDS editor - it's my object name. The type and _get and _set come from the code generator, a bit like how C# generates getters and setter for properties.
Calling in this becomes:
myOD.countslave_tiledata_set(56);

To make this all work, the code gen would also have to generate stub functions, that fit the _get and _set prototypes, and initialise the structure of function pointers with pointers to the stub functions. The stub functions can then follow a basic template, very similar to about e,g. for a uint16_t setter:
uint_16 countslave_tiltdata_get(void)
{
  return OD_readUint16(CO_OD_t *od, 0x2026, 2);
}

This approach has the following advantages:

No direct access (unless you really wanted to work around the API) to underlying memory, so can't (easily) bypass systems that need to know about change
Can ensure interested parties are notified of changes, simplying other areas of the CO stack e.g. PDO COS, heart beat times etc where changes to the OD need to be reflected into the stack, which quite often takes copies of OD data.
Can be auto-generated from the EDS file - you don't have to know whether to call CO_readUint16 or CO_readUint32 for example - easier not to get it wrong
Changes to types in EDS, without changes to app code would result in compiler warnings / errors - which is great - other solutions can fail silently
Can enforce read only - just don't generate the setter
Can support default initialisation - generate an _init with a write one implementation, which is called from CO_Init or similar with default values

If there is a concern about memory usage with all the function pointers requiring storage, and the runtime overheads of calling them, then it could be done with a whole bunch of macros using the pre-processor and still end up with type correctness, but it'd be less elegant imo.
An improvement on this might be to add another layer. Instead of having _get, _set and _init versions of all the object access functions, instead we could define types of (common) behaviour. e.g.
struct
{
  uint16_t(*get)();
} readonly_uint16;

struct
{
  uint32_t(*get)();
  void(*set)(uint32_t);
} rw_uint32;

This way, the OD and calling it is cleaner:
struct
{
  rw_uint32_t myvar1;
  readonly_uint16t myvar2;
} OD;

uint16_t value = OD.myvar1.get(); // Compiler warning uint32_t truncation
OD.myvar2.set(45);  // Compiler error - set doesn't exist

Also a nice to have in my opinion is something similar to the existing OD_extension function callbacks / property change system:
struct
{
  uint8_t(*get)();
  void(*onupdate)(uint8_t value);
} readonly_sync_uint8_t;

Which would allow the application to set an onupdate() handler, which would then be automatically called (if set) when the OD entry was changed by the stack... IT's limited to one interested party, so not quite a true event system, but could be extended possibly.
Just some ideas - hope it makes sense... They're not massively far off what we have now but if the EDS compiler/code gen could do some of the work for us, it'd make coding the app side of things to the OD simpler and quicker.

Thank you for your ideas.
I didn't start with new OD yet, so any ideas are welcome. Best approach may be a combination of multiple ideas.
I'm not very familiar with advantages of using getters and setters in the application. I may need some hints here.

struct
{
  rw_uint32_t myvar1;
  readonly_uint16t myvar2;
} OD;

This idea seems interesting from application side, but how to access OD from network side, with only known index and subindex? Maybe use a linked list?

My current idea is this:
typedef struct {
    uint16_t index;
    uint8_t no_of_entries;
    CO_OD_entry_t *entry;
} CO_OD_object_t;

typedef struct {
    const uint8_t subindex;
    const uint8_t dataType_or_something;
    const uint16_t attribute;
    uint8_t dataAccessType;
    void *pData;
} CO_OD_entry_t;

const CO_OD_object_t CO_OD[CO_OD_NoOfElements] = {
    {0x1000, 1, &OD_entry_1000},
    ...
}

OD_entry_1000[1] = { 0, 4, 0x1234, 0, (void*)&CO_OD_ROM.deviceType};
...

Something like this will be generated by Object Dictionary editor (libedssharp). It is very similar, what we have now. dataAccessType will always be 0 in the beginning, that will mean, pData is a simple pointer to initialized data.
Application will have the possibility to change dataAccessType and pData. For example, it can configure dataAccessType to 1 and set pData to preconfigured structure like this:
struct {
  void *object;
  uint32_t(*get)(void *object);
  void(*set)(void *object, uint32_t);
} OD_access_1_rw_uint32;

There will be getters and setters for the data access and they will behave differently for different dataAccessType.
That is a rough idea for now.

This idea seems interesting from application side, but how to access OD from network side

Yes, this approach is only helpful from the application side. It has no benefit and indeed makes it harder to access the OD from the network side. I would propose the underlying storage had 2 APIs, one for direct access (most useful for the application, but mayhave some use from the stack side for accessing known locations) and the (index/subindex) method for stack access.
The key here is allowing any access directly to the underlying data is potential unwanted because it allows data to change without anyone knowing about it. I note the COS system currently compares data buffers to see if anything has changed - the getter/setter system would allow that to bee removed as we could simply flag up the change whenever the application sets it.
I think the underlying storage approach for the OD is sound, the downsides are really only fairly minor in the application side i.e. that the user (of the API) has know things about the internals of the OD and remember things like the type, length and access properties of the OD members... which the getter/setter approach I suggested attempts to hide away and encapsulate with techniques that take advantage of compiler time type checking.
Regarding the network access - yes, you have to have some sort of reverse lookup system or search facility to convert index/subindex pairs into an OD entry. I don't know of any better way of doing this other than searching an array / list. Basically a dictionary pattern. I don't think we can assume there is a dictionary construct in the libraries that CANOpenNode currently depends on as it's dependencies on 3rd parts libs is minimal (which is good), so an implementation of a dictionary may be required to facilitate this. I don't think that is a bad thing however, it means that the exact mechanism of finding an OD entry from an index/sub-index is abstracted away from the SDO/PDO stack code and allows for 3rd parties to more easily evaluate, optimise or replace it for their chosen platform if for whatever reason there was deemed a need to do so. I think the current implementation relies on OD_find() or similar (sorry working from memory) so we're not a million miles away from this type of solution now, it just depends on whether you wanted to formalise it a bit...

How easy is it to extend / add to the code generation from libedssharp - is it a fairly closed black box or is it something we can extend? I have no familiarity with this lib development at all...

https://github.com/robincornelius/libedssharp basically reads standard CANopen device description file (with few extra properties) and generates pair of CO_OD.h/.c files. Don't want to put much extra stuff there. (I plan to move COS functionality away from there.)
libedssharp generates default values as initialized variables. This is useful for "factory reset". It is also easy to populate the variables from storage before CANopen objects are initialized. CANopen objects then have default values at initialization and CANopen objects can then specify own getters/setters for network side(SDO, PDO, storage). CANopen objects operate on own data and provide getters/setters for network access to own data.
With this approach there will also be no need for deeper change of the existing code, but existing code will be simplified.
Simple application can use OD variables directly - no need for getters/setters.

Sounds good, I'll be interested in how the COS stuff changes.
I see regarding libedssharp - it would be (a lot) more work to extend the code gen side... still I might have a play, see if there is any interest in new features in the libedssharp github project
Simple is always good :)

Actually, whilst we're on the subject - can I ask for some advice?? It's related to application development / accessing OD variables from directly on the master side (not a device/slave/server)
To make my motor move, requires a PDO, currently I do this:
OD_Motor_PDO_Data.pdo4_driveCtrl = 0x02;
OD_Motor_PDO_Data.pdo4_modeCtrl = 0x03;
OD_Motor_PDO_Data.pdo4_Ref16 = 250; //velocity;
OD_Motor_PDO_Data.pdo4_Ref32 = -98425; //position;
CO->TPDO[1]->sendRequest = 1;

Assuming COS is not set for these entries, is this the only / best way to send the PDO down to my device? Or have I missed something?

Yes, for now it is the only way to send PDO manually.
However, more usual approach to send time sensitive (motor data) is by using CANopen SYNC object and send synchronous PDOs. Intensive use of ChangeOfState mechanism can overload CAN in critical moments. With synchronous PDOs there is more traffic on CAN, but it is deterministic.
For new OD I plan a callback for PDO request (If variable will be mapped to some TPDO, then application will have an option to trigger it)

Ok cool - thanks v much

J1939 is supported by Linux kernel nowadays: https://www.kernel.org/doc/html/latest/networking/j1939.html. I'm not sure how well CANopen compares to J1939, but maybe that could give some extra insights?

I just glanced through the posts briefly and I want to add something important about getters/setters. I use C++ in my embedded applications exclusively, so I have some experience with object-oriented design. Part of my experience is also that C projects frequently approach object-oriented design in a suboptimal way (sorry...).
If you want to have getters/setters, then these functions must (and I really mean it - they MUST!) also get a pointer to some user provided state. This would be the equivalent of C++'s this and in C land it's just a standard void* context or maybe a pointer to the base OD_t* object, assuming that the user can easily "extend" or "inherit" it (this is tricky to do correctly in C, especially if most of the code would be auto-generated).
I'm writing this because the proposal from @jdormand from https://github.com/CANopenNode/CANopenNode/issues/162#issuecomment-589973899 does not have such pointers. While these pointers may seem useless in some cases, just imagine that the user wants to have the most trivial OD possible - each object is mapped to an element of an array (or a global variable). The only "twist" is that the OD is huge, say 1000 objects. If you have the extra pointer with the context, you can implement it entriely with two functions - one common setter and one common getter. Without this pointer you end up needing 2000 separate functions, which can never be inlined or optimized away (because the application stores the pointer to these functions) - 1000 setters and 1000 getters, each being basically identical.
As I've read @CANopenNode replies, CANopenNode code currently accesses underlying data of the object dictionary directly. Someone above proposed two APIs - one for the app, second one (raw API) for use by CANopenNode (I think having these 2 APIs would not be a perfect situation, but it's actually not that bad). But does CANopenNode really have to have direct access to the binary representation of the data? Let's say that there's a getter which returns uint32_t but CANopenNode just wants to get one byte from the middle. True, in this case the getter returns  4 times more data than required, but is this really a problem? The same can be said about setters - if there's a uint32_t setter and CANopenNode just wants to change one byte, why can't it read the whole entry, change this single byte and then write the whole entry again? API based on getters/setters indeed nicely solves the problem of notifying the application that the entry was changed by the network (but this can be also done with an user-supplied callback) and notifying the CANopenNode stack that the entry was changed by the application.
One last thing - I think it would be very hard to auto-generate generic code with custom getters/setters, while also making them object-oriented. This is probably much easier when doing like @jdormand proposed (just name the function like the object and expect the application to provide it), but how would that work in case of functions which require some additional state? No idea... It is probably much easier to just have optional, user-supplied callbacks executed when the entry is changed by the network (but please - also here the void* context is essential!). As these callbacks would be optional, the generator would not need to know anything about them - it would just add support for them in cases of objects which are writable from the network, but all callbacks would be disabled by default. In such scenario, the raw representation of the OD data can actually be hidden entirely from the application.

@FreddieChopin Thanks for the comments, I entirely agree that in a general case, implementation of OO in a non-OO language will require some manual implementation of the this pointer or equivalent, (context etc). However I don't see how that applies to this specific case in particular I don't see how extra context is appropriate to the code snippets I proposed:
myOD.countslave_tiltdata_set(56);
and
OD.myvar1.get()
as they already have context by virtue of the fact that they are members of a struct.
In the first case, which is a little clunky, the function pointer countslave_tiltdata_set is a member of the root OD struct. In the second, which is an improvement I think, the get is a member of the struct of which myvar1 is a type. In my example this was:
struct {   uint16_t(*get)(); } readonly_uint16;
Assuming this was what you were commenting on, could you clarify for me? Have I missed something?
The comment about overhead is absolutely fair, the downside of this approach is that every object dictionary entry would have (by the eds code generator) and additional function pointer for a ro or wo type, and 2 for a rw. In addition, there would be a lot of single line functions, in exactly the same way that C++ and C# would have, although thankfully in the latest C# it's all built in so a lot of the laborious typing is no longer required.
The question really becomes, do the benefits outweigh the costs - by forcing OB Dict access through a function, whatever the form is, the stack can react more effectively and efficiently to changes originating from the application/user code, and crucially could be auto-generated in. And conversely, changes to the OB Dict originating from the stack (network) could be effectively communicated (on an as needed basis) back to the user code, and the crucially the support for this also auto-generated.
That said @CANopenNode later comments on extending the code-generator are valid and sadly it may be difficult to extend the code generator in a way that the community in general would accept, so what then, a CANopenNode specific code generator?? Probably not :/

as they already have context by virtue of the fact that they are members of a struct.

This solves nothing, as there is absolutely no way to get back to this context from the function - in a generic way. If you would want to have a generic function - say "get_motor_speed" and your application would have 10 motors, it would not work. You would need 10 separate functions, each being basically identical. If you have just one function anyway, then you indeed don't need the context, but please don't forget about cases where there are multiple instances of similar functionality. We don't need to look very far - the example object dictionary of this repository has 8 digital inputs, 8 digital outputs, 12 analog inputs and 8 analog outputs. If you would have to implement it with a context-less functions, you would need 52 functions for this alone (I'm counting getters and setters). If the functions would get the context as an argument, all it would take is 6 functions.

In addition, there would be a lot of single line functions, in exactly the same way that C++ and C# would have

That's my main point. If the functions don't get the context you need as many functions as there are OD entries. If they get the context, in some cases one function can be used as a handler for many OD entries. You don't always need this context, but when you do there's no workaround for the case where this pointer is just not given to the function.

The question really becomes, do the benefits outweigh the costs - by forcing OB Dict access through a function, whatever the form is, the stack can react more effectively and efficiently to changes originating from the application/user code, and crucially could be auto-generated in. And conversely, changes to the OB Dict originating from the stack (network) could be effectively communicated (on an as needed basis) back to the user code, and the crucially the support for this also auto-generated.

I guess this can be solved in a slightly less user-friendly way, which is much more code-generator-friendly. How about having each entry in the following form:
struct OD_entry
{
... // whatever CANopenNode needs...
void (*notifyApplication)(struct OD_entry* entry);
}

By default the pointers to notifyApplication would be all NULL, and I'm assuming the user can replace them with whatever he/she likes (I have also a solution for a case where it would be preferrable for this pointer to be stored in flash memory). Whenever stack changes such entry, it would call this callback to notify the application about that event.
For the opposite case, I propose to have a global (there's no point in having pointers in each entry) function in a form:
void CO_OD_notifyStack(CO_t* co, CO_OD_t* od, struct CO_entry* entry);
(the list of arguments is obviously just an example, the entry is obligatory, the rest can be improved)
Whenever user changes one entry, it should call this function to notify the stack about that fact. This is slightly less convenient for the user (however there is no tragedy, it's quite good and easy to use), but I guess this requires zero tricky features in the object dictionary generator.

That said @CANopenNode later comments on extending the code-generator are valid and sadly it may be difficult to extend the code generator in a way that the community in general would accept, so what then, a CANopenNode specific code generator?? Probably not :/

Why not? Assuming that there's a separate tool for editing and creating EDS files, making a generator is not that hard really. libedssharp can be still used just to make EDS files, but we would have CANopenNode-specific tool just to convert an EDS file into source code. It would just be a simple python script which would read the EDS and use one of many template engines for python to output whatever sources you like. Personally I use Jinja2 and I think it's very good (I'm using it to generate board sources for my C++ RTOS, given the input in YAML file, similar in idea to what "devicetree" does for Linux). For the "parse EDS" part I hope that packages like canopen already have all the necessary functionality (and even if they don't, it's just a simple ini-like file anyway). Such generator would be also much easier to modify by the user, because the template files are just simple text files, pretty easy to understand (assuming you have some very basic knowledge about python - things like if-else, for loops, ...).

Thank you for your comments.
I'm talking about newOD for quite a long time and I still didn't start with it. However, I have some ideas and I'm also very thankful for your comments as they help to get better solution.
The thing is, I don't have 100% time for CANopen, I also have other things to do. And second thing, I'm currently consolidating some other parts of CANopenNode (split-driver branch, move some parts from CANopenSocket, etc.). As my time enables, I will start with newOD soon.
I can't comment precisely your last posts now, I have to start diging into newOD first.
Janez

Another thing as it seems you are changing quite a bit of code now: Add unit tests. We all make mistakes, and implementing test cases, especially for error cases, helps find them. After you have the tests, you can make changes to code with some degree of certainty not to break another thing.
Protocol stacks are normally a prime use case for unit testing as you have very clear input and output interfaces :-)
For simplifying tests you can use a test framework. This leaves you with writing expectations, like this:
#include "path/to/mock-turtle.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

using ::testing::AtLeast;                         // #1

TEST(PainterTest, CanDrawSomething) {
  MockTurtle turtle;                              // #2
  EXPECT_CALL(turtle, PenDown())                  // #3
      .Times(AtLeast(1));

  Painter painter(&turtle);                       // #4

  EXPECT_TRUE(painter.DrawCircle(0, 0, 10));      // #5
}

(example from here: https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md).
Can someone reccomend a "C" test framework? The example is google mock / C++, and that is a pain in the ... to use for C code.

Ceedling is the right tool for this job it uses unity and cmock. https://github.com/ThrowTheSwitch/Ceedling

I have currently no experience with unit testing. I make own tests when writing new code, but it is not the same.
There was an effort for new OD by someone else, but it stalled. There is also unit testing, but unfortunately stack is very changed. I didn't go into details there: https://github.com/IzidorM/CANopenNode_OD
Ceedling seems nice, is there any example for some part of CANopenNode or something similar?

There are some "Embedded code" examples on Ceedling in folder "examples".
The test code from your link is using Unity which is  used by Ceedling and part of the same open source test framework. You can look at unity and cmock doc to get what Ceedling do.

But please keep default build tool at "make", this is what comes with even the most obscure toolchain...

I just pushed quite a lot of new code (CiA309-3 command interface, CO_fifo and rewritten CO_SDOclient). I have been testing it during the creation and in the finish. It works fine so far.
However I think, unit testing could be quite useful, especially for some parts of the code.
If unit testing will be implemented in CANopenNode, I don't like to add it to base git repo.
I would rather add CANopenNode testing code into CANopenSocket repo, which can be used also for some other Linux examples.

Hello, I have started with the implementation of the CANopen safety protocol (EN 50325-5 / CiA 304) I will also be making a pull request soon. One problem I stumbled upon is the mapping. Basically it is similar to PDO mapping, with the strange difference that the mapping object is not a record but an array.
The thing is that you have to change the maximum subindex of the array to support variable mapping. But this is not possible in the current OD implementation. Currently, I treat it like a record as a workaround. Perhaps we will find a more suitable solution in the new OD implementation.

Yes, this is currently a limitation. It will be solved with new OD.
I have just finished other updates to CANopenNode and I'm starting with new OD now.
You safety protocol contribution will be very welcome.

FINALLY - new object dictionary interface is here.
It is specified by two files: 301/CO_ODinterface.h and 301/CO_ODinterface.c.
Currently there are only those two files, compilable and full documented, no integration into the stack yet.
Basic principle and all good things from previous implementation stays the same. Everything is simplified, optimized for low memory devices and missing features are added. SDOserver, PDO, application, etc., all will use the same read/write access. Simple global access is still possible. Object dictionary will be generated with libedsshap as before, standard xsd files will be used. I think, I found quite a nice solution. Please view the files and add comments or suggestions.
Next, it is necessary to update libedssharp and SDOserver, PDO and other objects, which use OD variables.

Very cool interface! I haven't tested it but what I've seen from the code looks promising.
One thing I notice in the new files but also in some of the already existing once is the C90 code conformance. There are some constructs that do not conform with Ansi C (e.g. flexible array member, usage of stdint.h, etc.). In my opinion we can drop the C90 support and move on to C99 (which is already 20 years old) since its already kinda broken.l

We've discussed this a few times (https://github.com/CANopenNode/CANopenNode/issues?q=c99), but never made a desicion. Only thing against is that some compiler manufacturers _cough cough Microchip cough cough _ are still not implementing it.

You scared me. I somehow still use Microchip based hardware.
So I tried to compile latest master with CO_ODinterface.c with Microchip XC32 and XC16 - and it worked. That is good enough for me.
I think, we can move to C99. Is there something, what could make problems to Microchip compilers?

Some news:
New OD interface is advancing steady. Already pushed CO_ODinterface seems OK. SDO server is rewritten. There is also some work with transferring "OD access functions" for each CANopen object. Then comes libedssharp with OD generator and finally testing.

News:
newOD branch is pushed. It compiles well, but doesn't work, because there is only blank object dictionary.
Current changelog:

New Object dictionary interface. It has very similar principles as before. All parts of CANopenNode objects, which works with OD entries, are rewritten.
New OD.h and OD.c files, replaces CO_OD files.
CANopen.c and CANopen.h files redesigned. Integration of "OD.h" is optional. Configuration of multiple object dictionaries is possible with one CANopen device. Interface is the same, with some changes to function arguments.
Rewritten SDO server. Object dictionary part is removed.
CO_Emergency is mostly rewritten. Now is much easier customization.
CO_NMT_Heartbeat is redesigned.

Now follows libedssharp. testing, and other objects: HB consumer, sync, PDO, time, SDO, trace

I saw the new CO_ODinterface.h/c implementation but didn't understand how it's actually used in the stack:
in CO_ODinterface.h I see OD_entry_t which compose the OD_t but in all the other parts of the stack objects are structures of type CO_OD_entry_t and the two are not simple casts.
Is it planned to switch the CO_OD_xx_t types to OD_xx_t or did I totally misunderstand the goal of this OD change?

CO_ODinterface.h/c in master branch is currently only for preview. Was.
But in branch newOD, there is a working and tested part of the stack with fresh CO_ODinterface.h/c.
So, anyone interested may view and test the new OD principles:

https://github.com/CANopenNode/CANopenNode/tree/newOD (SDO, NMT, HB producer)
https://github.com/robincornelius/libedssharp/raw/gh-pages/build/OpenEDSEditor-latest.zip

preferences -> CANOPENNODE_V3

there is also a brand new CANopen test suite and extended cocomm interface in CANopenSocket.

Hi Janez, everyone and sorry for the delay in replying but I took some
time to organize some ideas.
On Mon, 2020-10-26 at 11:49 -0700, Janez wrote:

CO_ODinterface.h/c in master branch is currently only for preview.
Was.
But in branch newOD, there is a working and tested part of the stack
with fresh CO_ODinterface.h/c.
So, anyone interested may view and test the new OD principles:
https://github.com/CANopenNode/CANopenNode/tree/newOD (SDO, NMT, HB
producer)

https://github.com/robincornelius/libedssharp/raw/gh-pages/build/OpenEDSEditor-latest.zip

preferences -> CANOPENNODE_V3
there is also a brand new CANopen test suite and extended cocomm
interface in CANopenSocket.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or unsubscribe.

I gave it a brief look and the new OD interface looks much better than
the old one, at least from coding perspective.
The entire project looks great and impressive.
I know that the CANOpen best seller devices still are small uC that in
a way or the other have strong requirements on RAM/FLASH size or
processing speed and usually don't use OSs so all kind of possible
optimizations are welcome (defines to short circuit parts of the code,
defines to easily access well known objects in OD ...).
But I think that, slowly and silently, this is changing: building
tailored custom distros for many uP is now easy and RAM/FLASH
availability increases and ...
Another consideration I'd put on the table is that the stack is only
one part (even if the great one) of the CANOpen application and, at
least to me, the most annoying part of coding is doing the same thing
many times. I mean that there are lots of devices where the only
difference is in the OD contents but the application that handles it
depends on a .c/.h file that has to be derived from a .eds and than
requires to rebuild the application.
This makes me think that it would be great to develop code that
directly eats a .eds file and dynamically creates the OD.
Is this somehow interesting for you?
If anyone wants to discuss about it please let me know.
With best regards,
Daniele.

Hi Daniele,
having OD build from a description file (eds, xml, ...) at run-time is a great thing for powerful controllers, most likely master/managers. This also makes software development much easier.
This will, however, also make your software impossible to run on controllers with less than about a megabyte of ram/flash. And on a system of that size I will make serious thoughts about using ethernet/mqtt or similar instead of can/canopen...
With CAN-XL 2048 Byte frame on the horizon, in my opinion using etherent protocols will be the way to go anyway :-)
best regards,
Martin

Hi Martin
On Fri, 2020-12-11 at 01:48 -0800, Martin Wagner wrote:

Hi Daniele,
having OD build from a description file (eds, xml, ...) at run-time
is a great thing for powerful controllers, most likely
master/managers. This also makes software development much easier.
This will, however, also make your software impossible to run on
controllers with less than about a megabyte of ram/flash. And on a
system of that size I will make serious thoughts about using
ethernet/mqtt or similar instead of can/canopen...
With CAN-XL 2048 Byte frame on the horizon, in my opinion using
etherent protocols will be the way to go anyway :-)
best regards,
Martin
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or unsubscribe.

yeah, of course CAN is not the latest media/protocol to use when you're
dealing with fast uPs with lots of RAM/flash.
The problem is that in many places CAN is largely used and
gateways/converters are required to let user replace their networks
with newer devices.
This is why I'm rising the question: convert from CANOpen to  another
media is not a big deal, but every device has it's own OD and I'd avoid
creating tons of applications just to deal with them when the
difference is so little. Many applications means lots of trouble IMO.
.eds files are not a big deal to parse so I'd like to know if someone
is interested in this to share the work load.
Of course, basing it on CANopenNode stack requires that you guys are
interested in it too and that's why I'm asking.
Cheers,
Daniele.

Hi Janez, everyone and sorry for the delay in replying but I took some time to organize some ideas.
On Mon, 2020-10-26 at 11:49 -0700, Janez wrote: CO_ODinterface.h/c in master branch is currently only for preview. Was. But in branch newOD, there is a working and tested part of the stack with fresh CO_ODinterface.h/c. So, anyone interested may view and test the new OD principles: https://github.com/CANopenNode/CANopenNode/tree/newOD (SDO, NMT, HB producer)
https://github.com/robincornelius/libedssharp/raw/gh-pages/build/OpenEDSEditor-latest.zip
preferences -> CANOPENNODE_V3 there is also a brand new CANopen test suite and extended cocomm interface in CANopenSocket. — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.
I gave it a brief look and the new OD interface looks much better than the old one, at least from coding perspective. The entire project looks great and impressive. I know that the CANOpen best seller devices still are small uC that in a way or the other have strong requirements on RAM/FLASH size or processing speed and usually don't use OSs so all kind of possible optimizations are welcome (defines to short circuit parts of the code, defines to easily access well known objects in OD ...). But I think that, slowly and silently, this is changing: building tailored custom distros for many uP is now easy and RAM/FLASH availability increases and ... Another consideration I'd put on the table is that the stack is only one part (even if the great one) of the CANOpen application and, at least to me, the most annoying part of coding is doing the same thing many times. I mean that there are lots of devices where the only difference is in the OD contents but the application that handles it depends on a .c/.h file that has to be derived from a .eds and than requires to rebuild the application. This makes me think that it would be great to develop code that directly eats a .eds file and dynamically creates the OD. Is this somehow interesting for you? If anyone wants to discuss about it please let me know. With best regards, Daniele.

You'd need some storage to read that OD file from: ROM, Flash memory. And a platform independent way to accomplish that.
Relying on platform specific code is actually not something we want, hence why the various drivers where removed from the project last year. So in the end you'd have to implement that yourselves too, unless you rely on someone else's driver.
At best we'd have a platform abstraction layer that works with any kind of uController, but currently the choice is to let that not be part of stack but rely on any kind of PAL within the driver code.

but every device has it's own OD and I'd avoid
creating tons of applications just to deal with them when the
difference is so little. Many applications means lots of trouble IMO.

Most likely, if your OD is that different between two devices, they actually are two different devices with different functionallity and different hardware. In that case, you will most likely have many differences in firmware anyway.
For mostly similar devices, you can have a OD implemented that fits multiple devices, and switch  the entry implementation and EDS for each device in runtime. Only problem is that CiA tool checks if there are more entries implemented in OD than EDS.

.eds files are not a big deal to parse so I'd like to know if someone
is interested in this to share the work load.

.eds is outdated, current format is XML CiA311. As @geoffrey-vl mentioned, this is not a problem of parsing, but of memory and functionallity for file storing.  You also need to transfer the file onto the CANopen device somehow in the first place.
Please don't misunderstand me. I think this functionallity would be great. I really missed it when building our CANopen manager software on top of CANopenNode. But I prefer to have to only introduce myselve into one stack that fits all instead of one small and one large one, even if that brings some compromises...

On Fri, 2020-12-11 at 03:01 -0800, Martin Wagner wrote:

but every device has it's own OD and I'd avoid
creating tons of applications just to deal with them when the
difference is so little. Many applications means lots of trouble
IMO.

Most likely, if your OD is that different between two devices, they
actually are two different devices with different functionallity and
different hardware. In that case, you will most likely have many
differences in firmware anyway.

I'll try to better explain myself: let's assume we want to use a well
tried CANOpen device but the network where it has to be connected is of
different kind (e.g. ethernet or other dedicated busses).
Obvious answer is the device doesn't fit the requirements so find out
one that works on the network in use but if this was the case I
wouldn't have asked.
So we need a gateway/converter. This "in-the-middle" device will have
an OD designed to listen for TPDOs sent by the end device, with SDO
client/server or whatever needed to configure and talk with the end
device. Let's forget for now the other side and how communication has
to be handled.
Now, it comes up that another end device that does something different
from the first one and so has a different OD can't be replaced: we need
another "in-the-middle" device to connect it to the network.
Assuming that the hardware for the "in-the-middle" devices will be the
same, the OS distro also, is the only available approach to create
different applications to handle this?

For mostly similar devices, you can have a OD implemented that fits
multiple devices, and switch the entry implementation and EDS for
each device in runtime. Only problem is that CiA tool checks if there
are more entries implemented in OD than EDS.

Of course: that's why only what's in eds/xml has to be exposed by the
application.

.eds files are not a big deal to parse so I'd like to know if
someone
is interested in this to share the work load.

.eds is outdated, current format is XML CiA311. As @geoffrey-vl
mentioned, this is not a problem of parsing, but of memory and
functionallity for file storing. You also need to transfer the file
onto the CANopen device somehow in the first place.

As you said, xml or eds, is not a problem when you have ram/flash,
speed and an OS that can help. Also, being this not an end device but a
"pc like" device, the idea is to load it as a configuration file saved
in flash. Instead of building an application, you configure it.

Please don't misunderstand me. I think this functionallity would be
great. I really missed it when building our CANopen manager software
on top of CANopenNode. But I prefer to have to only introduce myselve
into one stack that fits all instead of one small and one large one,
even if that brings some compromises...
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or unsubscribe.

No problem: I'm trying to collect information and every suggestion is
welcome even if it is a "you can't do it that way".
So is this idea so wrong?
Regards,
Daniele.

Hello Danielle,
I think, with new OD it is perfectly possible, what you suggest.
Object dictionary is basically an array of OD_entry_t objects and it can safely be generated on program start, from whatever data source. The only requirement by OD_find() function is, that array is sorted by index.
However, OD_find() is not used by the stack directly, it is used by application, so also this function can be customized. For example, configuration of object dictionary as linked list or even dynamic adding/removing of OD entries in run time may be possible.
CANopen.h and .c files works in two modes: with or without '#include OD.h'. In later case CO_config_t structure has to be filled with pointers to multiple OD_entry_t objects, which can be defined anywhere. Of course, even CANopen.h and .c files can be customized.
If a system with more advanced object dictionary usage will be made, for example generating object dictionary from eds on program start, it is probably best to be own project, with own git repo, built on top of CANopenNode.
Regards,
Janez

Helly Janez
On Fri, 2020-12-11 at 04:55 -0800, Janez wrote:

Hello Danielle,
I think, with new OD it is perfectly possible, what you suggest.
Object dictionary is basically an array of OD_entry_t objects and it
can safely be generated on program start, from whatever data source.
The only requirement by OD_find() function is, that array is sorted
by index.

Yeah, that's what I intended with "from a coding point of view".

However, OD_find() is not used by the stack directly, it is used by
application, so also this function can be customized. For example,
configuration of object dictionary as linked list or even dynamic
adding/removing of OD entries in run time may be possible.
CANopen.h and .c files works in two modes: with or without '#include
OD.h'. In later case CO_config_t structure has to be filled with
pointers to multiple OD_entry_t objects, which can be defined
anywhere. Of course, even CANopen.h and .c files can be customized.
If a system with more advanced object dictionary usage will be made,
for example generating object dictionary from eds on program start,
it is probably best to be own project, with own git repo, built on
top of CANopenNode.

Basically this is what I started doing.
First thing I'd like to know was if I was going nuts, and from your
answer it seems it's feasible.
Than I'd know if this could be of any interest also for you guys, at
least to share ideas.

Regards,
Janez
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or unsubscribe.

Thanks everybody for replaying and you're welcome if you have hints or
thoughts to share.
Best regards,
Daniele.

For me it is not actual at the moment. But I would certainly like to add references (into the readme.md) to related projects, especially, if they may be usable for others. Sharing ideas about the tools, we use in our projects, is the beauty of open source.

Also, being this not an end device but a
"pc like" device, the idea is to load it as a configuration file saved
in flash. Instead of building an application, you configure it

Come to think about it, CANopenNode is really not exclusively useful for uController kind of projects. With the stack supporting Linux devices, and basic master functionality, I bet more and more people will look to extend its purpose from a demo tool to something that is running on a daily basis. I think we should take that into account. I think the stack should not enforce having a statically compiled OD. Maybe its best for your project, but it stands correct that even a medior Cortex M4 has enough RAM,ROM,Flash to pull the trick of dynamically loading the EDS file that is for example 200kb big. I don't think at this stage many people will do that, but at least now its not possible because everything has to be pre-allocated with zero values.
On linux you'd be writing mostly dynamic application code, because it's running on a generic OS. You may generate the OD from EDS, I for example was thinking of loading a custom description from SQL(lite), and some other people may decide for their application they're OK with a fixed machine/network config.
The most important thing to tackle first is allowing to support dynamically generation of OD. I believe this is already possible with newOD, but I haven't looked at it myself. From their on, custom extensions and demo application code can be shared, for example within CANopenSocket.

Can someone reccomend a "C" test framework? The example is google mock / C++, and that is a pain in the ... to use for C code.

I've been using mbed greentea in the past. You may not like it since it is bound to mbed ecosystem. But, it would allow to kick an entire CI/CD flow whenever a PR appears. This flow could build the code, deploy it to a uController device, and run unit tests on the device itself.
Here is picture of some server room at some ARM office showing that in action:

Maybe its best for your project, but it stands correct that even a medior Cortex M4 has enough RAM,ROM,Flash to pull the trick of dynamically loading the EDS file that is for example 200kb big.

That's true. There are chips that could load such file dynamically. And do nothing more than that...
Please don't blur this issue, which is about supporting multiple instances of CAN on a single, statically linked application. There are dozens of options available for Linux and I don't think anyone is going to choose CANopenNode over - say - canopen python module, which does all that you want right now, with no need to extend it at all.

Maybe its best for your project, but it stands correct that even a medior Cortex M4 has enough RAM,ROM,Flash to pull the trick of dynamically loading the EDS file that is for example 200kb big.

That's true. There are chips that could load such file dynamically. And do nothing more than that...
Please don't blur this issue or this project, which is meant to be used on microcontrollers. There are dozens of options available for Linux and PCs and I don't think anyone is going to choose CANopenNode over - say - canopen python module, which does all that you want right now, with no need to extend it at all.

I did :) When sharing codebases between applications and platforms it's easier to choose for any kind of C/C++ stack instead of higher level python based implementations. It's not only about features... Furthermore, if linux is not one of the target platforms then why is its driver supported in mainline CANopenNode?

That's true. There are chips that could load such file dynamically. And do nothing more than that

We're using a cortex M4, but not with 200k+ RAM. We have 32k, which also includes the application stuff. On linux / PC like system you can use as much memory as you like compared to that :-) .

Furthermore, if linux is not one of the target platforms then why is its driver supported in mainline CANopenNode?

Linux is one of the target platforms. It's also the easiest target platform to obtain, which makes it a good candidate for the "default target".
On both the law of the minimum applies.

Furthermore, if linux is not one of the target platforms then why is its driver supported in mainline CANopenNode?

I'm not the author, so I can only guess, but I would say that mainly for testing.

We're using a cortex M4, but not with 200k+ RAM. We have 32k, which also includes the application stuff. On linux / PC like system you can use as much memory as you like compared to that :-) .

To clear things up : there is no need to load that entire EDS file into RAM. You'd still only allocate only the things that you've put into the EDS, so in the end the same OD is generated and allocated in RAM. During parsing there is no need to load the entire ASCII EDS file to RAM, you can parse it line by line I'd say. That's 100bytes?
I've applied such thing in the past on CortexM4, it's not impossible. I'm not sure that dynamically loaded OD doesn't consume that much more memory than statically allocated. It's mostly just differently allocated.
And on some systems you'd opt for dynamic allocation (linux, medium and high-end CortexM4 and up), where on others you'd opt for least possible memory footprint and the predictability and ease of static allocation (for example yours), or PIC32, or ...).
I do understand the extra complexity, we don't want yet more PRECOMPILER statements in the code, there are already much to consider.  I'm not asking "loading of EDS" from source. I'm questioning if we couldn't make an interface that allows the developer to choice how the OD is allocated? Instead of having the stack poking into the OD directly: go through the OD interface... More features will follow as from that point on the application developer is free in deciding how to allocate things.
It's not about that the stack is not working for linux, because it is, and linux is also clearly an important target platform to consider. From the README:
...
CANopenNode is written in ANSI C in object-oriented way. It runs on different microcontrollers, as standalone application or with RTOS. Linux implementation with CANopen master functionalities is included.

So it's really about: do we want this 'static/dynamic allocation' feature, or not? Next, do we want it to be included at this state, now that the OD is being reworked? Obviously adding it afterwards will be a bigger pain when considering backwards compatibility. But maybe we're fine with that... and the outcome may well be that it is not possible to support this anytime soon due to lack of manpower, or that it adds too much complexity for the majority of the current users at this moment. We may also decide to consider it a feature that may come in CANopenNode 5 or later...  As @ddario says, IT evolves, and you must take into account that the projects you've started years ago will be different than the projects in few years from now.

Linux is one of the target platforms. It's also the easiest target platform to obtain, which makes it a good candidate for the "default target".

That is correct. Linux is also very useful for testing and for developing. And Linux have one of the most standardized programming interfaces.
There have to be a "default target". There have to be a working reference.
However, Linux "driver" may not be most suitable as example, because it is quite complex. But there are other examples.

I'm not the author, so I can only guess, but I would say that mainly for testing.

I've written that driver for 1.3 stable branch to be used in productive environment, and we are using it for exactly that purpose :-)

To clear things up : there is no need to load that entire EDS file into RAM. You'd still only allocate only the things that you've put into the EDS, so in the end the same OD is generated and allocated in RAM. During parsing there is no need to load the entire ASCII EDS file to RAM, you can parse it line by line I'd say. That's 100bytes?

As said before, this is not a problem of parsing. It's a problem of transfering and storing the EDS. Of course, that can be solved, even on small controllers, but at what cost in parts and engineering.

I think, "dynamic allocation ability" is not a question any more, since newOD. It is available.
OD can be switched in running program at the communication reset (not tested, but should work).
OD of type OD_t is passed to SDO server, SDO client (and application) init functions.

Linux driver from @martinwag is default also in all later releases. And it is as it have to be.
In V2 I have rewritten also other parts of Linux interface (epoll, mainline, timers, events, ...), to be more suitable for productive environment. There is actually a very rich functionality.
Together with CiA309-3 (acsii gateway and base64 encoding is comming) we have very strong and responsive CANopen tool in Linux. It may interlace with CANopen for python. But I don't plan to go any further than that.
CiA309-3 is part of the CANopenNode, because it is intended to be suitable for any target platform.

On Tue, 2020-12-15 at 11:56 -0800, Geoffrey Van Landeghem wrote:
[snip]

The most important thing to tackle first is allowing to support
dynamically generation of OD. I believe this is already possible with
newOD, but I haven't looked at it myself.

Actually I'm working on it and I have some questions:

In OD_entry_t, the member odObject points to OD_obj_var_t or
OD_obj_array_t or OD_obj_record_t according to the object type entry
refers to. But if the object is of extended type it has to point to
OD_obj_extended_t which has a pointer to the real object. Wouldn't
be simpler if OD_obj_[var|array|record]_t have a member to point to
OD_obj_extended_t which is set to NULL if object is not of extended
type? It will cost the space of a pointer but reduce the cost to
evaluate if it is an extended object or not in order to access to
the real object.
When is an object of extended type? Or better, is it possible to
know if an object is of extended type reading its description in the
eds/xml file?
The memory used to store the real values of the objects has to be
aligned with the data types?

Thanks in advance,
Daniele.

Maybe I was too sparing with memory for pointers. I was really trying to reduce the memory for small devices. It may be simpler as you suggested, but I'm not quite sure. This is complex part anyway, but gives quite a lot of flexibility and usability. The most time consuming part is OD_getSub() function, which should not be used very often anyway.
see CO_extensionIO and CO_flagsPDO here:https://canopennode.github.io/CANopenSocket/md_doc_objectDictionary.html or https://github.com/robincornelius/libedssharp/blob/xdd/libEDSsharp/CanOpenNodeExporter_V4.cs
Don't fully understand the question, but I think, no alignment is required, all data are byte aligned. Except for simple microcontrollers, which may access data directly in application programs, all access is via read() and write() functions.

The central function used by all CANopen objects is
ODR_t OD_getSub(const OD_entry_t *entry, uint8_t subIndex, OD_subEntry_t *subEntry, OD_IO_t *io, bool_t odOrig);
It fills OD_subEntry_t and OD_IO_t structures, required by CANopen objects. If object dictionary would be something very different, then OD_getSub function could change, but OD_subEntry_t and OD_IO_t should stay. All structures inside #if defined OD_DEFINITION are then irrelevant.

Great thanks.
I'll check your linked docs on Monday.
Daniele.
On Fri, Dec 18, 2020, 17:06 Janez<EMAIL_ADDRESS>wrote:

The central function used by all CANopen objects is
ODR_t OD_getSub(const OD_entry_t *entry, uint8_t subIndex, OD_subEntry_t
*subEntry, OD_IO_t *io, bool_t odOrig);
It fills OD_subEntry_t and OD_IO_t structures, required by CANopen
objects. If object dictionary would be something very different, then
OD_getSub function could change, but OD_subEntry_t and OD_IO_t should
stay. All structures inside #if defined OD_DEFINITION are then irrelevant.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/CANopenNode/CANopenNode/issues/162#issuecomment-748177075,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AFKJVJ2GVNUJV7LAJB6QJTLSVN4XRANCNFSM4KMUQDGA
.

On Fri, 2020-12-18 at 07:49 -0800, Janez wrote:

Maybe I was too sparing with memory for pointers. I was really trying
to reduce the memory for small devices. It may be simpler as you
suggested, but I'm not quite sure. This is complex part anyway, but
gives quite a lot of flexibility and usability. The most time
consuming part is OD_getSub() function, which should not be used very
often anyway.

I see. I tried to reduce the complexity using structure inheritance and
even if it seems clearer I have to admit it doesn't change much the
complexity of that part of code.

see CO_extensionIO and CO_flagsPDO here:
https://canopennode.github.io/CANopenSocket/md_doc_objectDictionary.html
or
https://github.com/robincornelius/libedssharp/blob/xdd/libEDSsharp/CanOpenNodeExporter_V4.cs

If I get it right, the idea is to create a project with
ConOpenNodeExporter which embeds the right info in the xml
configuration file. In the project you decide which objects need
extensions.

Don't fully understand the question, but I think, no alignment is
required, all data are byte aligned. Except for simple
microcontrollers, which may access data directly in application
programs, all access is via read() and write() functions.

No problem, you answered anyway :-)
—

You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

If I get it right, the idea is to create a project with
ConOpenNodeExporter which embeds the right info in the xml
configuration file. In the project you decide which objects need
extensions.

Yes. EDSEditor is quite complex program. It can import or export different  CANopen  device description files in more or less standard file formats (ini, xml based). It can also export CANopenNode source files, old or new version and can export documentation.

Some news
Object Dictionary, EDSEditor, SDO server and SDO client with all surrounding code is now in final shape and tested. Latest code is in newOD branch (version 4.0).
Current master (version 2.0) is also updated with relevant updates from newOD branch, so SDOclient, gateway and socketCAN drivers are quite the same and also tested.
Object Dictionary and SDO server in version 2 is quite the same as in version 1.3 and below. It was not tested at this point. I plan to keep this version in current state, except for bugfixes.
Still unfinished: Heartbeat consumer, SYNC, PDO, TIME
For more information on new Object Dictionary and on latest SDO client and gateway usage see https://github.com/CANopenNode/CANopenSocket/tree/master/examples/basicDevice

On Mon, 2020-12-28 at 06:11 -0800, Janez wrote:

Some news
Object Dictionary, EDSEditor, SDO server and SDO client with all
surrounding code is now in final shape and tested. Latest code is in
newOD branch (version 4.0).
Current master (version 2.0) is also updated with relevant updates
from newOD branch, so SDOclient, gateway and socketCAN drivers are
quite the same and also tested.
Object Dictionary and SDO server in version 2 is quite the same as in
version 1.3 and below. It was not tested at this point. I plan to
keep this version in current state, except for bugfixes.
Still unfinished: Heartbeat consumer, SYNC, PDO, TIME
For more information on new Object Dictionary and on latest SDO
client and gateway usage see
https://github.com/CANopenNode/CANopenSocket/tree/master/examples/basicDevice
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

Hi Janez, thanks for the news.
As said, I'm trying to make the OD dynamically loadable and along the
road I found some ideas to refactor a little the new OD interface. If
you (or someone else) want to have a look, please checkout my branch on
https://github.com/ddario/CANopenNodeNewOD.git and let me know. Branch
is rebased on latest newOD from CANopenNode site.
That said, I have a question about how arrays are treated in OD: in
CiA306 1.3 I see: page 15 "In principle it is possible, that a list of
sub-object does not have consecutive sub-indexes. The value of sub-
index 00h shall always provide the highest sub-index implemented. In
contrast, the EDS entry SubNumber shall contain the number of sub-
indexes implemented, including the sub-index 00 h".
The example in the same page refers to object 0x1010 Store Parameters:
[1010]
SubNumber=2
ParameterName=Store Parameters
ObjectType=8
[1010sub0]
ParameterName=largest Sub-Index supported
ObjectType=0x7
DataType=0x0005
AccessType=ro
DefaultValue=0x4
PDOMapping=0
[1010sub4]
ParameterName=save manufacturer defined parameters
ObjectType=0x7
DataType=0x0007
AccessType=ro
DefaultValue=0x1
PDOMapping=0
which is an array and has 2 sub-indexes: 0 and 4.
If I read right the code which is in place now, the size of the array
comes from SubNumber and than querying sub-index 4 is not possible. Or
if it comes from DefaultValue of sub-index 0, also sub-indexes 1, 2 and
3 are available.
Best regards,
Daniele.

I took a quick look into your branch. I like some ideas.
You changed OD interface, as you suggested in comments above. I will take a deeper look later.
I'm not very in favor of adding OD_find() instead of OD_GET() macro inside CANopen.c. OD_GET() may reduce startup time in simple devices and add more flexibility in complex, with multiple OD. And OD_GET() can also be defined to use OD_find().
I'm also not in favor to not having "consecutive sub-indexes" for arrays.
Currently OD interface treats each sub-element of record separately, with own properties, including sub-index. So records may have gaps.
On the contrary, arrays are treated as simplified form of record and behave as standard C arrays. If one need gaps inside array, he can define own IO access function. Or use record in OD interface to have even more flexibility.
The same object can be treated as record in OD interface and as array in EDS file, if necessary. But this is issue for EDSEditor.
Is I said, I like some other ideas and I will take additional look into changes in OD interface.
The important target of CANopenNode are small microcontrollers and I try to keep things optimized, as much as reasonably possible.

Danielle,
I thought, I finished the OD interface and have somehow closed that chapter. And now you reopened it with your suggestions.
I hope, you understand my unwillingness for changes. However, I think, it is not good to be conservative, if there are better solutions. Also cooperation is better then making things alone. And OD interface is new anyway.
I hope, I'm not bothering with my philosophy.
I would like to include your contribution but there are some points to discuss:

As mentioned above, I would keep OD_GET() instead of OD_find() inside CANopen.c.

I would like to keep const in static const ODObjs_t ODObjs = {...} and static const OD_entry_t ODList[] = {...}. Those are large data structures, unchangeable. In some microcontrollers it is possible to put them into flash only, so they consume no RAM. const may also add some additional safety. Alternative to const may be a macro (for example CO_PROGMEM), which can be specified inside CO_driver_target.h. However, OD_obj_extended_t and OD_extensionIO_t should stay separated.

In OD_stream_t, void *dataObjectOriginal was renamed to void *data. But data is not enough descriptive. Application may use both "data": dataObjectOriginal for initial value and data, transferred via read and write functions.  dataObjectOriginal can be renamed to something else, but should be appropriately descriptive.

Regards,
Janez

Danielle,
I thought, I finished the OD interface and have somehow closed that chapter. And now you reopened it with your suggestions.
I hope, you understand my unwillingness for changes. However, I think, it is not good to be conservative, if there are better solutions. Also cooperation is better then making things alone. And OD interface is new anyway.
I hope, I'm not bothering with my philosophy.
I would like to include your contribution but there are some points to discuss:

As mentioned above, I would keep OD_GET() instead of OD_find() inside CANopen.c.

I would like to keep const in static const ODObjs_t ODObjs = {...} and static const OD_entry_t ODList[] = {...}. Those are large data structures, unchangeable. In some microcontrollers it is possible to put them into flash only, so they consume no RAM. const may also add some additional safety. Alternative to const may be a macro (for example CO_PROGMEM), which can be specified inside CO_driver_target.h. However, OD_obj_extended_t and OD_extensionIO_t should stay separated.

In OD_stream_t, void *dataObjectOriginal was renamed to void *data. But data is not enough descriptive. Application may use both "data": dataObjectOriginal for initial value and data, transferred via read and write functions.  dataObjectOriginal can be renamed to something else, but should be appropriately descriptive.

Regards,
Janez

On Sat, 2021-01-02 at 08:42 -0800, Janez wrote:

Danielle,
I thought, I finished the OD interface and have somehow closed that
chapter. And now you reopened it with your suggestions.
I hope, you understand my unwillingness for changes. However, I
think, it is not good to be conservative, if there are better
solutions. Also cooperation is better then making things alone. And
OD interface is new anyway.
I hope, I'm not bothering with my philosophy.

Absolutely not. I'm glad if I can somehow contribute.

I would like to include your contribution but there are some points
to discuss:
As mentioned above, I would keep OD_GET() instead of OD_find() inside
CANopen.c.

In my branch I opted for OD_find() to keep things uniform: dynamically
loading OD, to me, means that the xml/eds file describes the objects
which are all just objects and not SOMETHING0x1010 and would avoid the
use of macros for special objects to simplify the code.
I get your point about reducing the startup time but does this have
such an hard impact on it? Anyway feel free to stick to OD_GET() if you
think it's better.

I would like to keep const in static const ODObjs_t ODObjs = {...}
and static const OD_entry_t ODList[] = {...}. Those are large data
structures, unchangeable. In some microcontrollers it is possible to
put them into flash only, so they consume no RAM. const may also add
some additional safety. Alternative to const may be a macro (for
example CO_PROGMEM), which can be specified inside
CO_driver_target.h.

My idea was to move the global OD variable to be a const pointer to the
OD: if you build with OD.[h/c] you declare/define there the dictionary
and than set OD to point to it; for dynamic loading, it's enough to not
include the OD.[h/c] in the build and set pointer to the somewhere else
dynamically created OD.
This is also why I changed OD_GET to OD_find: macros are defined in
OD.h.
I had some troubles in my first tests having all those const which
seemed to cause failures related to OD_extensionIO_init(). I found that
removing them everything worked fine. Think that having a CO_PROGMEM
macro that can enable/disable const would be a good solution.

However, OD_obj_extended_t and OD_extensionIO_t should stay
separated.

I picked up your idea to keep the memory footprint as much reduced as
possible and from what I could see in the code, the two could be merged
saving memory usage.
I also didn't get why OD_obj_extended_t has a pointer to PDOFlags
instead of the real PDOFlags (this was another question I'd have
raised).
Can you better explain why the two should stay separated? So I can
better understand what you want to achieve?

In OD_stream_t, void *dataObjectOriginal was renamed to void *data.
But data is not enough descriptive. Application may use both "data":
dataObjectOriginal for initial value and data, transferred via read
and write functions. dataObjectOriginal can be renamed to something
else, but should be appropriately descriptive.

It's up to you: I think that a good comment in the structure
declaration is the better place to describe a variable and prefer to
reduce the names but it's my personal taste so, no problem if you
prefer a more descriptive name.

Regards,
Janez
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

On Sat, 2021-01-02 at 08:42 -0800, Janez wrote:

Danielle,
I thought, I finished the OD interface and have somehow closed that
chapter. And now you reopened it with your suggestions.
I hope, you understand my unwillingness for changes. However, I
think, it is not good to be conservative, if there are better
solutions. Also cooperation is better then making things alone. And
OD interface is new anyway.
I hope, I'm not bothering with my philosophy.

Absolutely not. I'm glad if I can somehow contribute.

I would like to include your contribution but there are some points
to discuss:
As mentioned above, I would keep OD_GET() instead of OD_find() inside
CANopen.c.

In my branch I opted for OD_find() to keep things uniform: dynamically
loading OD, to me, means that the xml/eds file describes the objects
which are all just objects and not SOMETHING0x1010 and would avoid the
use of macros for special objects to simplify the code.
I get your point about reducing the startup time but does this have
such an hard impact on it? Anyway feel free to stick to OD_GET() if you
think it's better.

I would like to keep const in static const ODObjs_t ODObjs = {...}
and static const OD_entry_t ODList[] = {...}. Those are large data
structures, unchangeable. In some microcontrollers it is possible to
put them into flash only, so they consume no RAM. const may also add
some additional safety. Alternative to const may be a macro (for
example CO_PROGMEM), which can be specified inside
CO_driver_target.h.

My idea was to move the global OD variable to be a const pointer to the
OD: if you build with OD.[h/c] you declare/define there the dictionary
and than set OD to point to it; for dynamic loading, it's enough to not
include the OD.[h/c] in the build and set pointer to the somewhere else
dynamically created OD.
This is also why I changed OD_GET to OD_find: macros are defined in
OD.h.
I had some troubles in my first tests having all those const which
seemed to cause failures related to OD_extensionIO_init(). I found that
removing them everything worked fine. Think that having a CO_PROGMEM
macro that can enable/disable const would be a good solution.

However, OD_obj_extended_t and OD_extensionIO_t should stay
separated.

I picked up your idea to keep the memory footprint as much reduced as
possible and from what I could see in the code, the two could be merged
saving memory usage.
I also didn't get why OD_obj_extended_t has a pointer to PDOFlags
instead of the real PDOFlags (this was another question I'd have
raised).
Can you better explain why the two should stay separated? So I can
better understand what you want to achieve?

In OD_stream_t, void *dataObjectOriginal was renamed to void *data.
But data is not enough descriptive. Application may use both "data":
dataObjectOriginal for initial value and data, transferred via read
and write functions. dataObjectOriginal can be renamed to something
else, but should be appropriately descriptive.

It's up to you: I think that a good comment in the structure
declaration is the better place to describe a variable and prefer to
reduce the names but it's my personal taste so, no problem if you
prefer a more descriptive name.

Regards,
Janez
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

In my branch I opted for OD_find() to keep things uniform: dynamically
loading OD, to me, means that the xml/eds file describes the objects
which are all just objects and not SOMETHING0x1010 and would avoid the
use of macros for special objects to simplify the code.

I get your point about reducing the startup time but does this have
such an hard impact on it? Anyway feel free to stick to OD_GET() if you
think it's better.

May I suggest you to use CO_MULTIPLE_OD. In that case you don't even include OD.h file. Instead you have to populate CO_config_t structure, where you have all freedom. In that case OD_GET() will pick data from that structure.

I picked up your idea to keep the memory footprint as much reduced as
possible and from what I could see in the code, the two could be merged
saving memory usage.

I also didn't get why OD_obj_extended_t has a pointer to PDOFlags
instead of the real PDOFlags (this was another question I'd have
raised).

Can you better explain why the two should stay separated? So I can
better understand what you want to achieve?

When I was inventing OD interface, there were even more complex solutions. I simplified them to current degree. But after some conversation I like your ideas of simplifying things.
My basic idea was to reduce RAM memory as much as possible, but reducing "PROGMEM" is not so important. But currently all those pointers are really more tangled than necessary.
Maybe you are right, joined OD_extensionIO_t and OD_obj_extended_t is a good solution and also PDOFlags can be real, not pointer.
I have a further idea of adding a configurable pointer OD_extension_t into OD_entry_t instead inside OD_obj_var_t. ODList[] can not be constant then, but doesn't matter much. And EDSEditor can be much simplified, because it won't have to provide OD_extension_t objects.
I think, also ODT_EVAR, ODT_EARR, ODT_EREC can be removed. And CO_PROGMEM should be used instead of const for large ODObjs structure.

It's up to you: I think that a good comment in the structure
declaration is the better place to describe a variable and prefer to
reduce the names but it's my personal taste so, no problem if you
prefer a more descriptive name.

There may be a confusion, so I prefer using more descriptive name for that particular case. Maybe better name could be dataOrig instead of dataObjectOriginal. I would also rename OD_obj_var_t->data into OD_obj_var_t->dataOrig. Just to be more intuitive.

In my branch I opted for OD_find() to keep things uniform: dynamically
loading OD, to me, means that the xml/eds file describes the objects
which are all just objects and not SOMETHING0x1010 and would avoid the
use of macros for special objects to simplify the code.

I get your point about reducing the startup time but does this have
such an hard impact on it? Anyway feel free to stick to OD_GET() if you
think it's better.

May I suggest you to use CO_MULTIPLE_OD. In that case you don't even include OD.h file. Instead you have to populate CO_config_t structure, where you have all freedom. In that case OD_GET() will pick data from that structure.

I picked up your idea to keep the memory footprint as much reduced as
possible and from what I could see in the code, the two could be merged
saving memory usage.

I also didn't get why OD_obj_extended_t has a pointer to PDOFlags
instead of the real PDOFlags (this was another question I'd have
raised).

Can you better explain why the two should stay separated? So I can
better understand what you want to achieve?

When I was inventing OD interface, there were even more complex solutions. I simplified them to current degree. But after some conversation I like your ideas of simplifying things.
My basic idea was to reduce RAM memory as much as possible, but reducing "PROGMEM" is not so important. But currently all those pointers are really more tangled than necessary.
Maybe you are right, joined OD_extensionIO_t and OD_obj_extended_t is a good solution and also PDOFlags can be real, not pointer.
I have a further idea of adding a configurable pointer OD_extension_t into OD_entry_t instead inside OD_obj_var_t. ODList[] can not be constant then, but doesn't matter much. And EDSEditor can be much simplified, because it won't have to provide OD_extension_t objects.
I think, also ODT_EVAR, ODT_EARR, ODT_EREC can be removed. And CO_PROGMEM should be used instead of const for large ODObjs structure.

It's up to you: I think that a good comment in the structure
declaration is the better place to describe a variable and prefer to
reduce the names but it's my personal taste so, no problem if you
prefer a more descriptive name.

There may be a confusion, so I prefer using more descriptive name for that particular case. Maybe better name could be dataOrig instead of dataObjectOriginal. I would also rename OD_obj_var_t->data into OD_obj_var_t->dataOrig. Just to be more intuitive.

On Mon, 2021-01-04 at 02:55 -0800, Janez wrote:

In my branch I opted for OD_find() to keep things uniform:
dynamically
loading OD, to me, means that the xml/eds file describes the
objects
which are all just objects and not SOMETHING0x1010 and would avoid
the
use of macros for special objects to simplify the code.

I get your point about reducing the startup time but does this have
such an hard impact on it? Anyway feel free to stick to OD_GET() if
you
think it's better.

May I suggest you to use CO_MULTIPLE_OD. In that case you don't even
include OD.h file. Instead you have to populate CO_config_t
structure, where you have all freedom. In that case OD_GET() will
pick data from that structure.

Mhh, now I sow it for the 1st time and looks promising. At least for
the default objects. Need to figure out how but could be a good choice.

I picked up your idea to keep the memory footprint as much reduced
as
possible and from what I could see in the code, the two could be
merged
saving memory usage.

I also didn't get why OD_obj_extended_t has a pointer to PDOFlags
instead of the real PDOFlags (this was another question I'd have
raised).

Can you better explain why the two should stay separated? So I can
better understand what you want to achieve?

When I was inventing OD interface, there were even more complex
solutions. I simplified them to current degree. But after some
conversation I like your ideas of simplifying things.
My basic idea was to reduce RAM memory as much as possible, but
reducing "PROGMEM" is not so important. But currently all those
pointers are really more tangled than necessary.
Maybe you are right, joined OD_extensionIO_t and OD_obj_extended_t is
a good solution and also PDOFlags can be real, not pointer.
I have a further idea of adding a configurable pointer OD_extension_t
into OD_entry_t instead inside OD_obj_var_t. ODList[] can not be
constant then, but doesn't matter much. And EDSEditor can be much
simplified, because it won't have to provide OD_extension_t objects.
I think, also ODT_EVAR, ODT_EARR, ODT_EREC can be removed. And
CO_PROGMEM should be used instead of const for large ODObjs
structure.

This sounds great. Looking forward to see it.

It's up to you: I think that a good comment in the structure
declaration is the better place to describe a variable and prefer
to
reduce the names but it's my personal taste so, no problem if you
prefer a more descriptive name.

There may be a confusion, so I prefer using more descriptive name for
that particular case. Maybe better name could be dataOrig instead of
dataObjectOriginal. I would also rename OD_obj_var_t->data into
OD_obj_var_t->dataOrig. Just to be more intuitive.

Ok.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

On Mon, 2021-01-04 at 02:55 -0800, Janez wrote:

In my branch I opted for OD_find() to keep things uniform:
dynamically
loading OD, to me, means that the xml/eds file describes the
objects
which are all just objects and not SOMETHING0x1010 and would avoid
the
use of macros for special objects to simplify the code.

I get your point about reducing the startup time but does this have
such an hard impact on it? Anyway feel free to stick to OD_GET() if
you
think it's better.

May I suggest you to use CO_MULTIPLE_OD. In that case you don't even
include OD.h file. Instead you have to populate CO_config_t
structure, where you have all freedom. In that case OD_GET() will
pick data from that structure.

Mhh, now I sow it for the 1st time and looks promising. At least for
the default objects. Need to figure out how but could be a good choice.

I picked up your idea to keep the memory footprint as much reduced
as
possible and from what I could see in the code, the two could be
merged
saving memory usage.

I also didn't get why OD_obj_extended_t has a pointer to PDOFlags
instead of the real PDOFlags (this was another question I'd have
raised).

Can you better explain why the two should stay separated? So I can
better understand what you want to achieve?

When I was inventing OD interface, there were even more complex
solutions. I simplified them to current degree. But after some
conversation I like your ideas of simplifying things.
My basic idea was to reduce RAM memory as much as possible, but
reducing "PROGMEM" is not so important. But currently all those
pointers are really more tangled than necessary.
Maybe you are right, joined OD_extensionIO_t and OD_obj_extended_t is
a good solution and also PDOFlags can be real, not pointer.
I have a further idea of adding a configurable pointer OD_extension_t
into OD_entry_t instead inside OD_obj_var_t. ODList[] can not be
constant then, but doesn't matter much. And EDSEditor can be much
simplified, because it won't have to provide OD_extension_t objects.
I think, also ODT_EVAR, ODT_EARR, ODT_EREC can be removed. And
CO_PROGMEM should be used instead of const for large ODObjs
structure.

This sounds great. Looking forward to see it.

It's up to you: I think that a good comment in the structure
declaration is the better place to describe a variable and prefer
to
reduce the names but it's my personal taste so, no problem if you
prefer a more descriptive name.

There may be a confusion, so I prefer using more descriptive name for
that particular case. Maybe better name could be dataOrig instead of
dataObjectOriginal. I would also rename OD_obj_var_t->data into
OD_obj_var_t->dataOrig. Just to be more intuitive.

Ok.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

Danielle,
thank you for being so persuasive. It is much better now. Relation between object dictionary (editor) is much simplified. Extension is optionally configurable only by application.
Makefile, getter and setter helper functions are also simplified, as you suggested.
Regards,
Janez

Danielle,
thank you for being so persuasive. It is much better now. Relation between object dictionary (editor) is much simplified. Extension is optionally configurable only by application.
Makefile, getter and setter helper functions are also simplified, as you suggested.
Regards,
Janez

Hi Janez,
On Wed, 2021-01-06 at 23:45 -0800, Janez wrote:

Danielle,
thank you for being so persuasive. It is much better now. Relation
between object dictionary (editor) is much simplified. Extension is
optionally configurable only by application.
Makefile, getter and setter helper functions are also simplified, as
you suggested.
Regards,
Janez
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

glad I could contribute somehow :-) and I wish I'll keep on helping.
Your project is well structured and maintained so great job.
I saw the changes but didn't have time to rebase my branch yet.
Saw that you kept the OD_obj_xxx_t types distinguished. Guess because
inheritance could complicate the editor?
Just to know, when do you plan to merge the newOD branch into master?
Regards,
Daniele.

Hi Janez,
On Wed, 2021-01-06 at 23:45 -0800, Janez wrote:

Danielle,
thank you for being so persuasive. It is much better now. Relation
between object dictionary (editor) is much simplified. Extension is
optionally configurable only by application.
Makefile, getter and setter helper functions are also simplified, as
you suggested.
Regards,
Janez
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

glad I could contribute somehow :-) and I wish I'll keep on helping.
Your project is well structured and maintained so great job.
I saw the changes but didn't have time to rebase my branch yet.
Saw that you kept the OD_obj_xxx_t types distinguished. Guess because
inheritance could complicate the editor?
Just to know, when do you plan to merge the newOD branch into master?
Regards,
Daniele.

Saw that you kept the OD_obj_xxx_t types distinguished. Guess because inheritance could complicate the editor?

It is more a taste. I didn't see much advantage with inheritance and trying to reduce memory size on 16 bit microcontroller with packing of members inside structure.

Just to know, when do you plan to merge the newOD branch into master?

I have about one month of work to finish PDO and some testing, but I can not promise much, because I may have some other jobs to do in time between. If necessary, non-dynamic PDOs could be quickly implemented, but I don't like to hurry.

Saw that you kept the OD_obj_xxx_t types distinguished. Guess because inheritance could complicate the editor?

It is more a taste. I didn't see much advantage with inheritance and trying to reduce memory size on 16 bit microcontroller with packing of members inside structure.

Just to know, when do you plan to merge the newOD branch into master?

I have about one month of work to finish PDO and some testing, but I can not promise much, because I may have some other jobs to do in time between. If necessary, non-dynamic PDOs could be quickly implemented, but I don't like to hurry.

On Mon, 2021-01-18 at 02:02 -0800, Janez wrote:

Saw that you kept the OD_obj_xxx_t types distinguished. Guess
because inheritance could complicate the editor?

It is more a taste. I didn't see much advantage with inheritance and
trying to reduce memory size on 16 bit microcontroller with packing
of members inside structure.

I was trying to find a way to get arrays fully compliant to regulations
(allowing the fact that like records they can have not consecutive
indexes) without impacting too much on memory size.
On the other side, I had the feeling that inheritance allowed to keep
cleaner OD_getSub().
If I find out a solution I'll post it so you can evaluate it.

Just to know, when do you plan to merge the newOD branch into
master?

I have about one month of work to finish PDO and some testing, but I
can not promise much, because I may have some other jobs to do in
time between. If necessary, non-dynamic PDOs could be quickly
implemented, but I don't like to hurry.

Me neither. Just asked to avoid rebasing many times.
Take the time needed.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

Again great job.
Best regards,
Daniele.

On Mon, 2021-01-18 at 02:02 -0800, Janez wrote:

Saw that you kept the OD_obj_xxx_t types distinguished. Guess
because inheritance could complicate the editor?

It is more a taste. I didn't see much advantage with inheritance and
trying to reduce memory size on 16 bit microcontroller with packing
of members inside structure.

I was trying to find a way to get arrays fully compliant to regulations
(allowing the fact that like records they can have not consecutive
indexes) without impacting too much on memory size.
On the other side, I had the feeling that inheritance allowed to keep
cleaner OD_getSub().
If I find out a solution I'll post it so you can evaluate it.

Just to know, when do you plan to merge the newOD branch into
master?

I have about one month of work to finish PDO and some testing, but I
can not promise much, because I may have some other jobs to do in
time between. If necessary, non-dynamic PDOs could be quickly
implemented, but I don't like to hurry.

Me neither. Just asked to avoid rebasing many times.
Take the time needed.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

Again great job.
Best regards,
Daniele.

I was trying to find a way to get arrays fully compliant to regulations
(allowing the fact that like records they can have not consecutive
indexes) without impacting too much on memory size.

I don't bother much about the arrays. If one needs special behavior of the array, he can use OD extension, read/write functions, which can handle data, attributes, gaps, abort messages, etc. In that case OD does not need to specify data for the variables. It is then only necessary, that OD editor produces correct EDS file to comply the standard.
Arrays could also be treated similar as records, just with common dataLength and maybe some magic to avoid dataOrig. This could enable gaps and own attribute for each subindex, but would cost additional memory. Records are defined as array of OD_obj_record_t:
typedef struct {
    void *dataOrig; /**< Pointer to data */
    uint8_t subIndex; /**< Sub index of element. */
    OD_attr_t attribute; /**< Attribute bitfield, see @ref OD_attributes_t */
    OD_size_t dataLength; /**< Data length in bytes */
} OD_obj_record_t;

Maybe something like this for arrays?
typedef struct {
    uint8_t *dataOrig0; /**< Pointer to data for sub-index 0 */
    void *dataOrig; /**< Pointer to array of data */
    OD_attr_t attribute0; /**< Attribute bitfield for sub-index 0, see
                               @ref OD_attributes_t */
    OD_size_t dataElementLength; /**< Data length of array elements in bytes */
    OD_size_t dataElementSizeof; /**< Sizeof one array element in bytes */

    OD_obj_array_sub_t *subIdx_attr; /**< pointer to array. */
} OD_obj_array_t;

typedef struct {
    uint8_t subIndex; /**< Sub indexes of element. */
    OD_attr_t attribute; /**< Attribute bitfields, see @ref OD_attributes_t */
} OD_obj_array_sub_t;

I was trying to find a way to get arrays fully compliant to regulations
(allowing the fact that like records they can have not consecutive
indexes) without impacting too much on memory size.

I don't bother much about the arrays. If one needs special behavior of the array, he can use OD extension, read/write functions, which can handle data, attributes, gaps, abort messages, etc. In that case OD does not need to specify data for the variables. It is then only necessary, that OD editor produces correct EDS file to comply the standard.
Arrays could also be treated similar as records, just with common dataLength and maybe some magic to avoid dataOrig. This could enable gaps and own attribute for each subindex, but would cost additional memory. Records are defined as array of OD_obj_record_t:
typedef struct {
    void *dataOrig; /**< Pointer to data */
    uint8_t subIndex; /**< Sub index of element. */
    OD_attr_t attribute; /**< Attribute bitfield, see @ref OD_attributes_t */
    OD_size_t dataLength; /**< Data length in bytes */
} OD_obj_record_t;

Maybe something like this for arrays?
typedef struct {
    uint8_t *dataOrig0; /**< Pointer to data for sub-index 0 */
    void *dataOrig; /**< Pointer to array of data */
    OD_attr_t attribute0; /**< Attribute bitfield for sub-index 0, see
                               @ref OD_attributes_t */
    OD_size_t dataElementLength; /**< Data length of array elements in bytes */
    OD_size_t dataElementSizeof; /**< Sizeof one array element in bytes */

    OD_obj_array_sub_t *subIdx_attr; /**< pointer to array. */
} OD_obj_array_t;

typedef struct {
    uint8_t subIndex; /**< Sub indexes of element. */
    OD_attr_t attribute; /**< Attribute bitfields, see @ref OD_attributes_t */
} OD_obj_array_sub_t;

But I still prefer using OD extension with read/write functions over extending array definition.

But I still prefer using OD extension with read/write functions over extending array definition.

On Mon, 2021-01-18 at 06:45 -0800, Janez wrote:

I was trying to find a way to get arrays fully compliant to
regulations
(allowing the fact that like records they can have not consecutive
indexes) without impacting too much on memory size.

I don't bother much about the arrays. If one needs special behavior
of the array, he can use OD extension, read/write functions, which
can handle data, attributes, gaps, abort messages, etc. In that case
OD does not need to specify data for the variables. It is then only
necessary, that OD editor produces correct EDS file to comply the
standard.

Yeah, you told me but I still have a feeling that this will give me
troubles with the dynamic creation of the OD. Maybe I'm wrong but I
feel something pinching my neck ...

Arrays could also be treated similar as records, just with common
dataLength and maybe some magic to avoid dataOrig. This could enable
gaps and own attribute for each subindex, but would cost additional
memory. Records are defined as array of OD_obj_record_t:
typedef struct {
void *dataOrig; /< Pointer to data */
uint8_t subIndex; /< Sub index of element. */
OD_attr_t attribute; /< Attribute bitfield, see @ref
OD_attributes_t */
OD_size_t dataLength; /< Data length in bytes */
} OD_obj_record_t;
Maybe something like this for arrays?
typedef struct {
uint8_t *dataOrig0; /< Pointer to data for sub-index 0 */
void *dataOrig; /< Pointer to array of data */
OD_attr_t attribute0; /< Attribute bitfield for sub-index 0,
see
@ref OD_attributes_t */
OD_size_t dataElementLength; /< Data length of array elements
in bytes */
OD_size_t dataElementSizeof; /**< Sizeof one array element in
bytes */
OD_obj_array_sub_t *subIdx_attr; /**< pointer to array. */

} OD_obj_array_t;

Talking about it: can you clarify me why you need both
dataElementLength and dataElementSizeof?
AFAICS they're only used in OD_getSub and to me it seems they should
carry the same information or am I missing something?

typedef struct {
uint8_t subIndex; /< Sub indexes of element. */
OD_attr_t attribute; /< Attribute bitfields, see @ref
OD_attributes_t */
} OD_obj_array_sub_t;
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

On Mon, 2021-01-18 at 06:45 -0800, Janez wrote:

I was trying to find a way to get arrays fully compliant to
regulations
(allowing the fact that like records they can have not consecutive
indexes) without impacting too much on memory size.

I don't bother much about the arrays. If one needs special behavior
of the array, he can use OD extension, read/write functions, which
can handle data, attributes, gaps, abort messages, etc. In that case
OD does not need to specify data for the variables. It is then only
necessary, that OD editor produces correct EDS file to comply the
standard.

Yeah, you told me but I still have a feeling that this will give me
troubles with the dynamic creation of the OD. Maybe I'm wrong but I
feel something pinching my neck ...

Arrays could also be treated similar as records, just with common
dataLength and maybe some magic to avoid dataOrig. This could enable
gaps and own attribute for each subindex, but would cost additional
memory. Records are defined as array of OD_obj_record_t:
typedef struct {
void *dataOrig; /< Pointer to data */
uint8_t subIndex; /< Sub index of element. */
OD_attr_t attribute; /< Attribute bitfield, see @ref
OD_attributes_t */
OD_size_t dataLength; /< Data length in bytes */
} OD_obj_record_t;
Maybe something like this for arrays?
typedef struct {
uint8_t *dataOrig0; /< Pointer to data for sub-index 0 */
void *dataOrig; /< Pointer to array of data */
OD_attr_t attribute0; /< Attribute bitfield for sub-index 0,
see
@ref OD_attributes_t */
OD_size_t dataElementLength; /< Data length of array elements
in bytes */
OD_size_t dataElementSizeof; /**< Sizeof one array element in
bytes */
OD_obj_array_sub_t *subIdx_attr; /**< pointer to array. */

} OD_obj_array_t;

Talking about it: can you clarify me why you need both
dataElementLength and dataElementSizeof?
AFAICS they're only used in OD_getSub and to me it seems they should
carry the same information or am I missing something?

typedef struct {
uint8_t subIndex; /< Sub indexes of element. */
OD_attr_t attribute; /< Attribute bitfields, see @ref
OD_attributes_t */
} OD_obj_array_sub_t;
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

Maybe I'm wrong but I feel something pinching my neck ...

Maybe you are right. But why don't we just add fourth element OD_obj_array_2_t or something. In your dynamic creator and larger data capacity you simply use that instead of OD_obj_array_t And simply add another case in
ODR_t OD_getSub(....) {
    switch (entry->odObjectType & ODT_TYPE_MASK) {
    ...
    case ODT_ARR_2: {

Talking about it: can you clarify me why you need both dataElementLength and dataElementSizeof?
AFAICS they're only used in OD_getSub and to me it seems they should carry the same information or am I missing something?

Just to make sure. Some microcontrollers may be quite strange sometimes.

Maybe I'm wrong but I feel something pinching my neck ...

Maybe you are right. But why don't we just add fourth element OD_obj_array_2_t or something. In your dynamic creator and larger data capacity you simply use that instead of OD_obj_array_t And simply add another case in
ODR_t OD_getSub(....) {
    switch (entry->odObjectType & ODT_TYPE_MASK) {
    ...
    case ODT_ARR_2: {

Talking about it: can you clarify me why you need both dataElementLength and dataElementSizeof?
AFAICS they're only used in OD_getSub and to me it seems they should carry the same information or am I missing something?

Just to make sure. Some microcontrollers may be quite strange sometimes.

On Mon, 2021-01-18 at 07:41 -0800, Janez wrote:

Maybe I'm wrong but I feel something pinching my neck ...

Maybe you are right. But why don't we just add fourth element
OD_obj_array_2_t or something. In your dynamic creator and larger
data capacity you simply use that instead of OD_obj_array_t And
simply add another case in
ODR_t OD_getSub(....) {
switch (entry->odObjectType & ODT_TYPE_MASK) {
...
case ODT_ARR_2: {

Yeah, that would be another solution.
I'm trying to avoid as much as possible changes on the stack so trying
all possible paths :-)

Talking about it: can you clarify me why you need both
dataElementLength and dataElementSizeof?
AFAICS they're only used in OD_getSub and to me it seems they
should carry the same information or am I missing something?

Just to make sure. Some microcontrollers may be quite strange
sometimes.

Ok. Now is clear.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

Thanks,
Daniele.

On Mon, 2021-01-18 at 07:41 -0800, Janez wrote:

Maybe I'm wrong but I feel something pinching my neck ...

Maybe you are right. But why don't we just add fourth element
OD_obj_array_2_t or something. In your dynamic creator and larger
data capacity you simply use that instead of OD_obj_array_t And
simply add another case in
ODR_t OD_getSub(....) {
switch (entry->odObjectType & ODT_TYPE_MASK) {
...
case ODT_ARR_2: {

Yeah, that would be another solution.
I'm trying to avoid as much as possible changes on the stack so trying
all possible paths :-)

Talking about it: can you clarify me why you need both
dataElementLength and dataElementSizeof?
AFAICS they're only used in OD_getSub and to me it seems they
should carry the same information or am I missing something?

Just to make sure. Some microcontrollers may be quite strange
sometimes.

Ok. Now is clear.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

Thanks,
Daniele.

Some news:
HB consumer, Time and Data storage is finished. Also everything was quite tested, especially SDO block transfer.
Important part still missing: PDO and SYNC.

Important part still missing: PDO and SYNC.

Is there any expected date for the PDO and SYNC parts to be integrated to the newOD branch or the master branch?

Important part still missing: PDO and SYNC.

Is there any expected date for the PDO and SYNC parts to be integrated to the newOD branch or the master branch?
Thanks!

In master branch both should be working. At least I'm using latest commit in master, PDO is OK.

In master branch both should be working. At least I'm using latest commit in master, PDO is OK.

Thanks for the update regarding the master branch.
Is there also any info regarding the expected date for the PDO and SYNC parts to be integrated to the newOD branch?
I am looking forward to integrate this new solution in our device.
BR.

I can say with 90% certainty, newOD will be finished in one month or earlier.

One request, as I don't see it implemented in the new branch. Please pass some argument to all the CO_LOCK_...() and CO_UNLOCK_...() macros (for example pointer to CO_CANmodule or sth unique/separate for the used object dictionary). The reason I'm asking for that is that the new OD interface will probably make it possible (or get closer) to allowing use of multiple CANopenNode instances (e.g. 2 separate CAN interfaces, each with its own, completely independent, object dictionary). To achieve this goal all the global objects must be removed (moved into some context-structs) and all global functions should become context-aware. Please don't forget about it while doing this rework...

One request, as I don't see it implemented in the new branch. Please pass some argument to all the CO_LOCK_...() and CO_UNLOCK_...() macros (for example pointer to CO_CANmodule or sth unique/separate for the used object dictionary). The reason I'm asking for that is that the new OD interface will probably make it possible (or get closer) to allowing use of multiple CANopenNode instances (e.g. 2 separate CAN interfaces, each with its own, completely independent, object dictionary). To achieve this goal all the global objects must be removed (moved into some context-structs) and all global functions should become context-aware. Please don't forget about it while doing this rework...

Done. All the CO_LOCK_...() and CO_UNLOCK_...() macros accepts  CO_CANmodule_t as argument. Also locking Object Dictionary is clarified. This is implemented only in newOD branch, but not in previous branches.

Now I can say, all vital parts of CANopenNode are finished. PDO and SYNC are rewritten and tested to some degree.
The only missing things are "304/SRDO", which is advanced object and "extra/CO_trace", which will be moved to own repo.
NewOD is mostly new code and I recommend it for new designs. Things are better specified as in previous versions. But it is new code and it may still hide some bugs.
"newOD" will stay for some time separate git branch. Default "master" branch, now also named "v2.0-master" still points to previous version. Use git checkout newOD or something to switch branches.
CANopenSocket is updated to the latest newOD branch. There are some examples and more documentation. More will come.
Most significant improvement with new Object Dictionary is usage of fast read/write functions for all accessing of OD variables. Also PDO use functions for mapped variables. This gives a lot of interesting options to application code. Object dictionary entries are now accessible as objects - there is no more need to access global variable space. So, ecpecialy with custom CANopen.h/c files, many advanced combinations of CANopen objects are possible, including multiple object dictionaries.

Thank you very much for your work, this is a great new ! I will try this soon =)

Ok I can do some testing on PIC too when you will share your branch

CANopenPIC dsPIC33 example update to newOD is available on https://github.com/CANopenNode/CANopenPIC/pull/8 this is still a work in progress but it is compiling without errors.