Validate CAPL Text File I/O Before Trusting a CSV Export
The problem: successful calls can still produce wrong data
A file opens, several reads succeed, and the chart looks plausible. That does not establish that the intended input was read, that each read was a complete record, or that the export contains every sample. This walkthrough separates those questions using small disposable fixtures that can be checked by hand.
The scope is a local CANoe/CAPL workflow with one numeric timestamp per physical text line. It is not a general CSV parser, binary trace converter or distributed test setup. The API behavior below is based on the authorized Vector reference available to this site. The test matrix is an original proposed procedure; no CANoe compilation or execution results are claimed.
Decide the contract before opening a file
Write down the expected directory, filename, encoding, timestamp unit, maximum record length, number of records and handling of blank lines. Keep a fixture separate from measurement originals. For the first run use ASCII digits and a decimal point so encoding questions do not obscure record-boundary failures.
| Decision | Suitable choice | Failure prevented |
|---|---|---|
| Recreate a complete disposable export | Text overwrite, mode 0 | Accidental inclusion of an older recording |
| Continue a known text file | Append, mode 2, only after checking its final separator and continuity | Concatenated records or timestamp resets |
| Preserve evidence of a line boundary | fileGetString and bounded record assembly | Mistaking a chunk for a complete line |
| Read known short lines without LF in the result | fileGetStringSZ, with a validated length limit | Manual newline removal; it does not remove the need for overflow checks |
| Read a binary capture | A format-aware binary workflow | Treating arbitrary bytes as numeric text |
openFileRead searches database directories before the active configuration directory according to the available reference. openFileWrite takes a relative filename and uses the configured write path, or the configuration directory by default. These are different resolution rules. Reading back the same basename is not sufficient proof that you reopened the output you just wrote.
Use distinctive fixture names and sentinel content to demonstrate the resolved source. Set up and verify the destination before opening; do not assume directories will be created. The available setWritePath reference excludes distributed environments. Check your installed version before adapting the workflow to that environment.
Keep three stages separate
The useful processing order is: successful read → complete bounded record → valid numeric value. A successful read does not skip the middle step.
For example, with a buffer capacity of 8, a reader can return the first seven characters of the line 123456789. Those seven characters are a valid number, but they are the wrong record. Do not update sample counts or statistics until the complete record has been established. If an input record exceeds the documented limit, reject the import with its record number. If you choose to skip it instead, discard through that record's boundary before starting another; a remaining suffix is not a new record.
fileGetString includes LF when it encounters the line boundary; fileGetStringSZ does not include LF. Both are bounded by buffsize - 1. The original CANoe 12 help for fileGetStringSZ lists LF and CRLF (DOS) as line endings. The following tests compare both fixtures and their returned bytes; accepted input forms alone do not establish identical byte normalization in every installed version.
Invalidate the current application record before each attempted read and parse only after success. Do not depend on a failed call clearing the buffer. Otherwise, reading past the last valid record can reuse its old contents and create a duplicate timestamp.
The documented read result is 1 or 0, with 0 described as an error. This extracted reference does not establish a separate result that proves clean EOF. Record an unexplained stop as read termination. For a fixture, compare the known count and contents; for production completeness, use diagnostics documented by your installed version or another independently established completeness check. A partially read file is not automatically a valid smaller dataset.
Run a matrix of small fixtures
In the table, \n describes an LF separator and \r\n describes CRLF; create actual separators, not the literal backslash characters. These are designed inputs and expected application behavior, not test results.
| Fixture | What to observe | Acceptance condition |
|---|---|---|
0\n10\n20\n |
Chunks, accepted records and final read status | Exactly three values, in that order; no repeated last value |
| The same values using CRLF | Returned characters and parsed values | Same numeric records under the declared line-ending policy |
| Empty file | Whether any previous buffer contents are processed | Zero values; explicit empty-input outcome |
0\n\n10\n |
A blank physical record | Explicit skip or rejection policy; never silently convert it to zero |
0\n10 without a final separator |
Final partial-line handling | An explicit tested policy: accept the complete final number or reject; no data invented from a later failed read |
| Records at and beyond the chosen capacity boundary | Chunk length and assembly state | Whole record accepted only within the declared limit; no split timestamp |
| A missing filename | Open return value | No read attempt through handle 0 |
| Two same-named fixtures in possible search locations | Which sentinel is read | Source location is unambiguous before further tests |
Existing disposable output containing OLD\n |
Mode 0 versus mode 2 | Overwrite and append behavior match the chosen contract |
Existing output ending in 20, then append 30\n |
Whether records concatenate | The application prevents or rejects the unsafe append |
Save the raw fixture, CANoe version, active configuration, selected API variant, buffer capacity, open/read/write/close statuses and accepted row count. Logging only the final mean hides the evidence needed to explain a failure.
Write status is not a byte count
filePutString receives a character count and returns 1 on success or 0 on error. A successful request for 20 characters still returns 1. Do not compare the return with 20 or retry the record because those values differ.
Use the length of the intended valid text, not the capacity of its storage. Include record separators explicitly. Stop declaring success after any failed write; do not assume a retry is safe because the documented result does not say whether zero bytes were written. After writing, check fileClose and inspect the actual file. Preserve a prior write failure even if close succeeds.
One operation should own each handle. Every successful open needs cleanup on normal completion, parse rejection and user abort. Do not overwrite an open handle with a new one or reuse it after confirmed closure. If cleanup fails, report it separately from the original failure and stop that operation from using the handle further.
Connect validated timestamps to interval analysis
Once the round trip is correct, use a synthetic single-column file containing 0, 10, 20 and 31, in milliseconds. The adjacent differences are 10, 10 and 11 ms. Check those pairs manually, then compare them in the message interval analyzer.
The analyzer accepts its documented timestamp-list or single-column CSV format; it does not interpret a general CANoe log with channel, ID and payload fields. Extract the timestamp column, retain original order and agree on units first. For real data, select one continuous message stream as described in measuring a CAN message period. File integrity does not prove that the selected traffic answers the intended timing question.
See sources and validation limits. Check signatures, availability and any missing EOF or encoding details against the help shipped with your installed CANoe version before using the procedure in a test that must certify completeness.