Add Product Store Component to enable save/restore for a selection of data products - #196
Add Product Store Component to enable save/restore for a selection of data products#196owhite7128 wants to merge 4 commits into
Conversation
11c1efc to
0f89ae7
Compare
| Pkt : Packet.T; | ||
| Stat : constant Serialization_Status := Self.Packets.Stored_Products (Self.Sys_Time_T_Get, Self.Bytes.all (Self.Bytes.all'First .. Self.Store_Last), Pkt); | ||
| begin | ||
| -- This should never fail since both the autocoder and an assertion at Init |
There was a problem hiding this comment.
is there such an assertion at Init?
| type: Crc_16.Crc_16_Type | ||
| format: U16 | ||
| skip_validation: True | ||
| byte_image: True |
There was a problem hiding this comment.
I think i'd rather this live with src/util/crc/. One less dir to add to the path for build performance, and it makes some sense to cluster it with the utility.
| # (i.e. a matching recomputation). Requiring always valid types | ||
| # guarantees a restore can never inject a data product whose use | ||
| # downstream raises a constraint error: | ||
| if not entry.data_product.type_model.is_always_valid(): |
There was a problem hiding this comment.
This is good. We should belt and suspenders it though. Add a compile time error to the generated Ada package which prevents compilation if somehow a possibly invalid type sneaks in.
| -- The data product could not be saved and the existing slot contents | ||
| -- are not trustworthy, so zero the slot: | ||
| else | ||
| -- The data product could not be saved, so zero the slot: |
There was a problem hiding this comment.
What is the logic behind this? Intuitively to me, if we have issue fetching the data product, we should do nothing. Leave whatever the last value that was good was from the store. Overwriting with zeros could mean anything, and seems better to keep the old value.
| Idx := @ + Time_Length; | ||
|
|
||
| -- Restore each data product entry: | ||
| for Item of Self.Store_Description.Entries.all loop |
There was a problem hiding this comment.
Unless I am missing something, I think we have an issue here. What if a data product was never stored. Whatever is in MRAM should not be restored to the database. Ideally, we would do nothing in that case, and leave the data product as Not_Available in the database.
The issue is that I think we would need some meta data to indicate that a DP is written, which doesn't currently exist. In the store you could include a byte for each DP that you write a zero or nonzero value for to indicate if the DP has been saved or never saved. We can trust that this store is not garbage if we CRC over it, so that if the CRC is good, we know this meta data store is meaningfull. Let's chat before you implement this. There could be better ideas.
| end if; | ||
| -- An entry stores its data product's timestamp if and only if that | ||
| -- timestamp is what the entry is restored with: | ||
| pragma Assert (Item.Store_Timestamp = (Item.Restore_Time = Use_Stored_Dp_Time)); |
There was a problem hiding this comment.
If this is true, do we need Store_Timestamp boolean at all? Could we just use Restore_Time to do the logic we need?
There was a problem hiding this comment.
Maybe we want this, just in case we want to store the DP timestamp for dump. Weird use case, but maybe OK.
| begin | ||
| -- This should never fail since the autocoder and the Store_Size_Type | ||
| -- constraint guarantee that the store fits within a single packet: | ||
| pragma Assert (Stat = Success); |
There was a problem hiding this comment.
I still don't see an assertion Init that proves this comment. Where is it?
| end if; | ||
|
|
||
| -- Increment the tick counter, rolling over at Ticks_Per_Save: | ||
| Self.Tick_Count := (@ + 1) mod Self.Ticks_Per_Save; |
There was a problem hiding this comment.
Seems like this should be gated by Save_On_Tick to be more correct and save computation if Save_On_Tick is False.
| use Command_Execution_Status; | ||
| begin | ||
| Self.Save_On_Tick := True; | ||
| Self.Event_T_Send_If_Connected (Self.Events.Save_On_Tick_Enabled (Self.Sys_Time_T_Get)); |
There was a problem hiding this comment.
Seems like we should reset count related to this here.
|
One somewhat large design issue that is not addressed: No atomicity across a save. Do_Save overwrites the store in place and writes the CRC last. A reboot mid-save corrupts the entire store, so the Set_Up restore fails and all products are lost, not just the ones being written. Power loss during a write is fairly central to this component's mission. The CRC-last ordering means it fails safe, which is good, but a double-buffered layout (two copies, alternate writes, restore the valid/newer one) would survive it. We should discuss this as an option. AI suggests: Alternatively, we go to a best effort mode instead, and potentially drop the CRC altogether. |
|
A few AI flagged issues worth looking at: |
- Remove No_Time save time option; a save time is always written - Default restore_time to Use_Save_Time; require Use_Stored_Dp_Time when store_timestamp is True (and vice versa) - Require stored data product types to be always valid in the model - Add Enable_Save_On_Tick/Disable_Save_On_Tick commands and events - Add Save_Count, Restore_Count, Crc_Invalid_Count data products, seeded at Set_Up - Add Ticks_Per_Save init parameter to divide down the save rate; dispatch queued commands after tick business logic - Zero the slot of any data product that cannot be saved (removes prior-CRC preservation logic); make Id_Out_Of_Range an unmaskable dedicated event - Make Do_Restore a Boolean function; inline one-line helpers - Replace component-local store_crc_error with shared framework type Crc_Mismatch_Info in src/types/crc - Rename main test model to specific-named backup.test_assembly .stored_products.yaml, verifying multi-instance model support - Remove test_no_time suite (No_Time removed); add tests for seeding, enable/disable, tick divider, and id-out-of-range; 15/15 tests pass, 100% implementation coverage, style clean Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add a stored length byte to each store entry (zero = never saved). Entries that cannot be fetched during a save keep their previous contents; a save onto an invalid store sanitizes it first so garbage can never carry a nonzero stored length - Silently skip never-saved entries on restore, leaving those data products unavailable in the database; refuse entries whose stored length mismatches the model (new Stored_Length_Mismatch event), which detects stored products model changes across boots - Emit compile-time Always_Valid checks in the generated table package - Move crc_mismatch_info to src/util/crc alongside the CRC utility - Gate the tick divider under Save_On_Tick and reset it on re-enable, so enabling deterministically saves on the next tick - Restate the store-fits-in-packet guarantee as an Init assertion - Tests: new suites for restore-skips-unwritten and stored-length mismatch, per-id fetch failure control in the tester, divider freeze/reset coverage; 17/17 tests pass, 100% implementation coverage, style clean Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The store previously held a single copy written in place with the CRC written last. A reboot mid-save failed safe (the restore refused the corrupted store) but lost every stored product. The store is now double buffered: each save writes the copy NOT holding the most recent valid save, stamps it with a monotonic save counter inside the CRC-protected region, and writes the CRC last. A restore validates both copies and reads from the valid copy holding the newest counter, so a mid-save reboot costs at most one save interval of freshness. - Init now takes two byte array pointers (Bytes_A/Bytes_B), each at least Store_Size bytes, so the copies can be placed in separate memory banks. - Dump_Store emits two packets (Stored_Products_A/B), one per copy, dumped as-is for ground inspection. - Products_Saved and Products_Restored events report the copy and its save counter (Store_Copy_Info.T); Store_Crc_Invalid reports a CRC mismatch per copy (Copy_Crc_Mismatch_Info.T) when a restore finds no valid copy. - Slots whose data product cannot be fetched during a save now keep the values from the most recent valid save via copy seeding. - New tests: mid-save reboot recovery (ping-pong fallback and recovery) and oversized byte arrays with nonzero first indices. - Also addresses review feedback: implementation spec doc drift, dead code in stored_products.py, and a clear ModelException (with case-insensitive package parsing) when product_store_packets cannot resolve the stored products model. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
45adb97 to
78d3129
Compare
This component adds the functionality to save a set of data products to specified memory region. It has the ability to restore data products with different set times depending on the desired behavior.