Skip to content

Implement support for i24/u24 (#61)#62

Merged
mcbridejc merged 8 commits into
mcbridejc:mainfrom
rohel01:u24-i24-support
May 8, 2026
Merged

Implement support for i24/u24 (#61)#62
mcbridejc merged 8 commits into
mcbridejc:mainfrom
rohel01:u24-i24-support

Conversation

@rohel01
Copy link
Copy Markdown
Contributor

@rohel01 rohel01 commented Mar 19, 2026

Hello,

I must support u24 in my application.
I started implementing it, but I am not sure it is complete (I do not understand the complete project yet)
Happy to receive directions!

Cheers,

@rohel01
Copy link
Copy Markdown
Contributor Author

rohel01 commented Mar 20, 2026

On this one, I am not sure I have fully understood what needs to change to support new datatypes.
Help welcome!

Comment thread zencan-common/src/objects.rs
@mcbridejc
Copy link
Copy Markdown
Owner

One question that comes to mind is whether we store non-standard bit widths as the next largest rust type, or using e.g. arbitrary_int.

I do think this could get a little involved. We have to work through what SDO transfers look like with new sizes. Also, PDOs right now are making the simplifying assumption there every mapping is a multiple of 8-bits.

@rohel01
Copy link
Copy Markdown
Contributor Author

rohel01 commented Apr 1, 2026

One question that comes to mind is whether we store non-standard bit widths as the next largest rust type, or using e.g. arbitrary_int.

I do think this could get a little involved. We have to work through what SDO transfers look like with new sizes.

So my first implementation used the next largest rust type, but now that I have added integration tests, I see the issue: the current scalar field implementation (in object_dict/sub_objects.rs) relies on the rust type to perform size checks.

I could add an extra argument to the macro to capture the "real" field size, but I believe it would be cleaner to model new field types with... types. So, I am currently exploring using the arbitrary_int crate as you suggested.

Also, PDOs right now are making the simplifying assumption there every mapping is a multiple of 8-bits.

This should not be an issue right? All CANopen standard type sizes are multiple of 8-bits.

@mcbridejc
Copy link
Copy Markdown
Owner

I could add an extra argument to the macro to capture the "real" field size, but I believe it would be cleaner to model new field types with... types. So, I am currently exploring using the arbitrary_int crate as you suggested.

Makes sense to me.

Also, PDOs right now are making the simplifying assumption there every mapping is a multiple of 8-bits.

This should not be an issue right? All CANopen standard type sizes are multiple of 8-bits.

No...for example there is signed/unsigned24 :). Actually, I think it is possible as far as the protocol is concerned to only copy some bits to a PDO, e.g. I think you can map just the bottom 12-bits of a 16-bit object. The size of each mapping is specified in bits, not bytes. When I implemented it, I just decided to reject non-byte-aligned mappings to keep things simple, assuming I would go back to it later as needed. I guess we could continue in that spirit and implement 24-bit types but enforce the constraint that they cannot be PDO mapped.

@rohel01 rohel01 force-pushed the u24-i24-support branch from 3cbf24b to 1527c02 Compare May 4, 2026 12:21
@rohel01
Copy link
Copy Markdown
Contributor Author

rohel01 commented May 4, 2026

Also, PDOs right now are making the simplifying assumption there every mapping is a multiple of 8-bits.

This should not be an issue right? All CANopen standard type sizes are multiple of 8-bits.

No...for example there is signed/unsigned24 :). Actually, I think it is possible as far as the protocol is concerned to only copy some bits to a PDO, e.g. I think you can map just the bottom 12-bits of a 16-bit object.

Sorry, I still do not understand this. signed24 and unsigned24 types use 24 bits as specified so they fit on 3 bytes (3*8=24). Why do you think they are not multiple of 8 bits?

I just pushed a working implementation of SDOs with I24 and U24 data, including tests. Can you start looking at the code? I believe I might be introducing some technical debt. For example, I see the storage size of types seem to be specified in multiple places.

@rohel01 rohel01 force-pushed the u24-i24-support branch from 1527c02 to 072c864 Compare May 4, 2026 12:57
@mcbridejc
Copy link
Copy Markdown
Owner

Sorry, I still do not understand this. signed24 and unsigned24 types use 24 bits as specified so they fit on 3 bytes (3*8=24). Why do you think they are not multiple of 8 bits?

No, don't be sorry. This was just me failing to engage brain. You're right, of course. I thought maybe there was a 12-bit type...but no...not that either!

I just pushed a working implementation of SDOs with I24 and U24 data, including tests. Can you start looking at the code? I believe I might be introducing some technical debt. For example, I see the storage size of types seem to be specified in multiple places.

Great! I will have a more detailed look soon!

@rohel01
Copy link
Copy Markdown
Contributor Author

rohel01 commented May 5, 2026

PDOs with u24/i24 seem to be working out of the box. Updated tests to showcase this

Comment thread zencan-common/src/objects.rs Outdated
Comment thread zencan-node/Cargo.toml Outdated
}
}

macro_rules! impl_arbitrary_int_field {
Copy link
Copy Markdown
Owner

@mcbridejc mcbridejc May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a separate storage type here? Can't we just use the existing macro and store it as a ScalarField<u24>? The arbitrary_int::u24 already handles storage right? And... I think pushing the u24 creation up moves a panic path out of the common crate which makes it more visible and -- I'm not sure, maybe LTO handles it anyway -- but maybe more likely for the compiler to be able to prove it can't panic and remove it.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK...I see one snag is read_size needs a different implementation because we can't do size_of::<u24>(). Hmm...

Copy link
Copy Markdown
Owner

@mcbridejc mcbridejc May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love duplicating the same read/write methods. I pushed a commit with an approach that uses a trait to define the read size for the types, and then we can share a single impl_scalar_field macro. Let me know if you see any issue with that.

Copy link
Copy Markdown
Contributor Author

@rohel01 rohel01 May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is the technical debt I was referring to. I have extended your approach to also prevent duplication in the SDO client. However, this is still not optimal, see get_rust_type_and_size in codegen.rs where type sizes are duplicated. I guess we can live with that minor duplication though

@rohel01 rohel01 force-pushed the u24-i24-support branch from 9c97454 to 64ada79 Compare May 6, 2026 08:29
@rohel01 rohel01 force-pushed the u24-i24-support branch from 64ada79 to 5d365bf Compare May 6, 2026 08:41
@rohel01 rohel01 marked this pull request as ready for review May 6, 2026 08:48
@mcbridejc mcbridejc merged commit be415df into mcbridejc:main May 8, 2026
2 checks passed
@mcbridejc
Copy link
Copy Markdown
Owner

Thanks!

@mcbridejc mcbridejc changed the title Start implementing support for i24/u24 (#61) Implement support for i24/u24 (#61) May 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants