Table of Contents

Workflow: Sensor Data Mapping

This workflow covers mapping external sensor data (dynamometer, smart tool holder, accelerometer) to simulation toolpaths so that simulation steps can index real-world measurement data.

flowchart TD
    Prepare["Prepare sensor CSV data"]
    Configure["Configure time mapping"]
    Simulate["Run simulation"]
    Map["Map data to simulation steps"]
    View["View mapped results"]
    Synth["No controller log?<br>Synthesise the controller table<br>from the simulated timeline"]

    Prepare --> Configure --> Simulate --> Map --> View
    Simulate -.-> Synth -.-> Map

Overview

Data mapping associates external sensor measurements with simulated machining steps. After mapping, each step can reference real-world force, torque, and acceleration data for:

Depending on data volume and application, mapping is either one-to-one (each step maps to one data point) or one-to-many (each step maps to multiple data points from high-sampling-rate sensors).

The mapping is a handful of session-shell calls. In the web application the two file mappings are mission commands of their own and the other calls go into Script commands — From the web application — and when the controller wrote no log, the simulation's own timeline stands in for it — When there is no controller log. A file whose columns carry the vendor's own titles maps as it came, with those titles named on the command; only a unit other than the reader's calls for a converted copy — A vendor file.


1. Prepare Sensor CSV Data

Sensor Data Format

The sensor file is what the series mapper reads — Strategy C, Strategy D and the second layer of the two-layer chain. It needs a header row with one time column and the channels it carries:

Channel Accepted headers Unit
Time ActualTime, ActualDateTime, TimeTag or Time — the first of these present is the one read see below
Force (dynamometer) Fx, Fy, Fz, or Workpiece.Fx, Workpiece.Fy, Workpiece.Fz N
Moment (smart tool holder) Mx, My, Mz, or Holder.Mx, Holder.My, Holder.Mz N·m
Acceleration Ax, Ay, Az g — read as g-units; no step property or chart of the product uses this channel
ActualTime,Mx,My,Mz
18:23:54.703,-0.02923,0.10733,0.00409
18:23:54.704,0.04155,-0.04457,0.00448
...

A time cell takes one of three forms. A plain number is seconds. <hours>:<minutes>:<seconds>.<fraction> is a timecode — a time of day when the recorder writes its clock that way. A cell that carries a date is an absolute instant, rebased through the project's mapping anchor: the anchor is set once, to 00:00 of the day of the first absolute instant any conversion in the project sees, so a same-day recording also becomes a time of day. The anchor is saved with the project and is not cleared by ClearTimeMappingData(); a project that anchored on one day and now maps a recording made on another needs MappingAnchorDateTime = null; in a script before the run, or an explicit value.

Numbers are read with a period as the decimal separator on every machine, whatever its language settings — the same way the product writes them; a recorder that exports 11,5 needs converting.

A channel the file does not carry reads NaN. Any other column is ignored by this reader — it keeps time, force, moment and acceleration and nothing else. A column that should reach the steps as a custom field belongs in the controller file below, whose extra columns are embedded into the steps, or in a CSV played directly (Strategy B).

Controller Data Format (for Two-Layer Mapping)

The controller CSV must contain FileNo, LineNo and one of three time columns:

FileNo,LineNo,ActualTimecode,ActualDateTime,MC.X,MC.Y,MC.Z,...
1,6,00:00:00.030,2026-03-16 15:57:45.559000,0,0,0.37,...
Time column What it carries
ActualTimecode The run-relative timecode written by WriteStepFiles(API). ActualDateTime, the absolute controller instant, rides beside it and is optional.
ActualTime The legacy single column, still read. A bare timecode cell is used as-is; an absolute date-time cell is rebased through the mapping anchor's converter.
EndTimecode The simulated end timecode, used as a fallback when neither of the above parses. Files written before that rename carry AccumulatedTime, which is also accepted.

A header row with none of the three is refused by name rather than mapped to nothing. A file this product exported carries the first pair, not the legacy column — which is why a step file plays back and maps without editing its headers.

FileNo and LineNo are both 1-based. FileNo numbers the files in the order the play enters them: the main program is 1, the first subprogram it calls is 2, and every call is a file of its own — a subprogram called twice takes a new number on its second call. The counter rewinds at every Reset, so the numbering is the same on every run. LineNo is the line within that file.

Several rows may carry the same (FileNo, LineNo). The line's steps then take times interpolated linearly from the first row's time to the last row's, the two end steps keeping the rows' own times; one row per line stamps every step of the line with that one time. Every other column — the machine coordinates MC.X … MC.C excepted — becomes a step variable: its values are embedded into each step of the line, interpolated the same way, and the column is registered under its title (no unit, format G4), so the charts, the tool-path colors and Step Properties can pick it as soon as the mapping is done. A column registered before the mapping — with RegisterStepVariable and a name, unit and format of your own — keeps that registration. A column named like a built-in step property (Feedrate_mmdmin, ToolId, …), or under the reserved MC. prefix (an axis beyond MC.C), is not mapped: the step keeps its own value, and the mapping names such columns in one session message; rename the column in the CSV header to map it. A step file this product wrote carries the built-in properties as simulated values, so mapping one back adds only its other columns.

Column Titles Are Configurable

The headers in the two tables above are the defaults, not a requirement on the file. Each mapping call takes a configuration that names the file's own titles, one per column the reader looks up — MapSingleCsvConfig(API) for the controller file, MapSeriesCsvConfig(API) for the sensor file:

MapSingleByCsvFile("Mapping/controller.csv", new MapSingleCsvConfig {
    FileNoTag = "ProgramNo", LineNoTag = "BlockNo", ActualTimeTag = "Timestamp" });
MapSeriesByCsvFile("Mapping/sensor.csv",
    new MapSeriesCsvConfig { TimeTag = "Clock", MzTag = "Tq" });

Only the columns that differ need naming; a tag left alone keeps its default, and a blanked tag is its default again.

File Tag Default title
Controller FileNoTag, LineNoTag FileNo, LineNo
Controller ActualTimecodeTag, ActualDateTimeTag ActualTimecode, ActualDateTime
Controller ActualTimeTag ActualTime
Controller SimTimeTag EndTimecode — AccumulatedTime is accepted with it only while the tag is left at this default
Controller MachineCoordinatePrefix MC. — the machine-coordinate columns are the prefix plus X, Y, Z, A, B, C
Sensor TimeTag ActualTime, then ActualDateTime, TimeTag, Time
Sensor FxTag, FyTag, FzTag Fx … or Workpiece.Fx …
Sensor MxTag, MyTag, MzTag Mx … or Holder.Mx …
Sensor AxTag, AyTag, AzTag Ax, Ay, Az

Two rules differ between the files. In the controller file a tag simply replaces the title, and the three time sources keep their order — a log with one time column is mapped by naming it in ActualTimeTag, whose cell may be a timecode or a date-bearing instant. In the sensor file a tag left at its default keeps every accepted header of its row above and the channel stays optional, while a changed tag is looked up exactly and the file must carry it: a header row without it is refused with the title named, so a mistyped title cannot pass for a channel the recording does not have.

A controller column the configuration names is consumed; every other column is still embedded into the steps under its own title — one named like a built-in step property excepted, as above — so renaming a tag never renames a custom field.


2. Configure Time Mapping

Strategy A: One-to-One Mapping (MapSingleByCsvFile)

MapSingleByCsvFile reads a file in the controller format above and uses time interpolation to map each data row to one simulation step.

PlayNcFile("NC/file1.nc");
MapSingleByCsvFile("Data/controller.csv");

Strategy B: One-to-One via PlayCsvFile

PlayCsvFile can drive the simulation directly from CSV data, where each row becomes one step. Every column the play does not read as a command rides the row's step and is registered as a step variable, so the charts can pick it; a column named like a built-in step property, or under the reserved MC. prefix, is left out, and the play names those columns once in the session messages.

PlayCsvFile("Data/controller.csv");

Strategy C: One-to-Many Global Mapping (MapSeriesByCsvFile)

For high-sampling-rate data, first establish ActualTime via one-to-one mapping, then map the series:

PlayNcFile("NC/file1.nc");
MapSingleByCsvFile("Data/controller.csv");  // establishes ActualTime
MapSeriesByCsvFile("Data/sensor.csv");       // maps high-rate series

Strategy D: One-to-Many Local Mapping (Anchor-Based)

For mapping a recording onto specific segments of the NC path, with the segment's ends anchored in the program itself.

Step 1 — Name the recording windows. Each key pairs a file with a time window of it; the window is stretched linearly over the steps the anchors select, which is what corrects the drift the tip in §4 describes.

ClearTimeMappingData();
AddTimeDataByFile("lineA", "Mapping/sensor1.csv", "18:25:51.7100", "18:26:12.9910");
AddTimeDataByFile("lineB", "Mapping/sensor1.csv", "18:26:30.5750", "18:27:12.2880");

A window's ends are seconds, timecodes, or date-bearing instants. A key already in the table is not replaced — the call returns false.

Step 2 — Anchor the segments in the NC program. The anchors are C# calls written into the program's own comments. The runner's comment syntax takes the comment out of the block first — parentheses on a Fanuc-family controller (Fanuc, Mazak, Syntec), ; to the end of the line on Siemens, //, ; or parentheses on Heidenhain — and the script syntax then takes the script out of the comment's text by two markers: @@ for a script that runs before the block's motion, @@^ for one that runs after it. The two markers are settings of that syntax rather than fixed characters — Script Commands in NC Code.

G01 X13. F20 (@@LineSelection("lineA", FirstTouch, ShiftTime_s(2), LineEnd, ShiftDistance_mm(-1));)
G01 X25. F10 (@@LineSelection("lineB", FirstTouch, null, LastTouch, null);)

For a segment across several lines, open it before the first line's motion with BeginSelection and close it after the last line's with EndSelection:

G01 Z-0.2 F1500 (@@BeginSelection("region1", FirstTouch, null);)
...
G01 X10. Y10. (@@^EndSelection("region1", LineEnd, null);)

A parenthesised comment runs from the first ( to the last ) on the line, so the parentheses inside the call are no problem — but keep the anchor the only parenthesised text on its line, since a second comment on the same line would be swallowed into the script. On Siemens the same anchor is written after ;:

G01 X13. F20 ;@@LineSelection("lineA", FirstTouch, null, LineEnd, null);

The names in the calls — FirstTouch, LineEnd, ShiftTime_s, null — are the session shell's. An embedded script compiles against the same references and imports as a Script command, so whatever a Script command can call, an anchor can call too. Five rules decide whether an anchor does what it looks like it does:

  • FirstTouch looks forward from the Begin anchor. The first-contact search starts at the step where the anchored line begins and runs to the End anchor, so a Begin anchor may sit on the first feed line of the segment rather than on the line that actually reaches the stock. LastTouch searches backward from the End anchor the same way. A segment in which the search finds no contact step maps nothing, and nothing says so.
  • One key opens and closes once until a Reset, within one program. A second EndSelection on a key already closed, or an EndSelection whose BeginSelection never ran, throws when the line ends; the run reports it at that line as Line-End--Exception, and that segment is not mapped. A program that plays the same segment twice needs two keys. A begin that no end has closed is dropped when the session starts its next program — the mission's next Program File, or the next PlayNcFile of a Script command — so a key's two anchors belong in one program and the subprograms it calls.
  • Reset clears the anchors; a Stop does not. The host that holds the selections is emptied when the session is reset, with the per-step mapped samples; the key → window table of Step 1 and the mapping anchor date are not, which is why ClearTimeMappingData() opens the script. A Start after a Stop with no Reset between plays the mission again on the same session: a key the stopped run had closed keeps the segment that run selected, and the replay reports Line-End--Exception at its EndSelection; a key the stopped run left open — it stopped between the key's two anchors — is dropped as the replay starts, so the replay's own BeginSelection and EndSelection pair. To map the program again, press Reset before the Start.
  • The anchors need no licence feature of their own — but packages 3.2.7 to 3.2.31 ran them only under NcComposition: without it the first embedded script reported Script--NotLicensed once and every further one was skipped silently, so the anchors selected nothing and nothing was mapped. Later packages run them unconditionally, as 3.1 did.
  • An anchor that does not compile is reported on its step as Script-Compile--Error; the mission's compile check covers Script commands only, not the comments in a program.

Anchor Flags:

Flag Description
LineBegin Motion start point of the line
LineEnd Motion end point of the line
FirstTouch First contact with the workpiece
LastTouch Last contact with the workpiece

Offset Options:

Offset Description
null No offset
ShiftTime_s(<seconds>) Time-based offset (positive = forward)
ShiftDistance_mm(<mm>) Distance-based offset (positive = forward)
Warning

;@… was the marker of the previous runner. On Siemens, or inside parentheses on a Fanuc-family controller, a comment holding it is an ordinary comment and the anchor does nothing; written bare on a Fanuc-family controller it is leftover text and the build also warns UnparsedText--Remaining. Either way the build warns CsScript-Marker--Legacy once per NC file, naming the file and the first line where ;@ stands before a call or an assignment. Rewrite the marker as @@ inside the controller's comment: (@@…) on a Fanuc-family controller and on Heidenhain, ;@@… on Siemens.

Two reports date a build from before the anchors worked on the SoftNc runner. An anchored step reporting Script-Run--Exception (“BeginSelection has to be used in Command Source File”) is a runner that did not yet publish the line boundary the selection calls rely on: the anchors were parsed and ran, and mapped nothing. An anchored line reporting Syntax-Build--Exception with the run turning into rapid traverses is older still: the block carrying the marker lost its motion word and played as the previous modal rapid. On either build, map with the two-layer chain — a real controller log or a synthesised controller table (When there is no controller log).

Map on Selection End

EnableMapOnSelectionEnd controls automatic mapping when a selection ends (default: true):

EnableMapOnSelectionEnd = true;  // EndSelection triggers Map automatically

Clearing Mapping Data

The key → window table of Strategy D and the mapping anchor are saved with the project and survive a Reset; the per-step mapped samples and the anchors in the program do not — the next run rebuilds them. Clear the table before adding to it, so a re-run does not find its keys already taken:

ClearTimeMappingData();

The anchor date is not part of what this clears — see Sensor Data Format.


3. Run Simulation

PlayNcFile("NC/file1.nc");
Note

Why interpret NC code instead of playing CSV directly? The system NC interpreter produces more accurate simulation paths than direct controller CSV playback, which has limited sampling resolution that distorts tool paths.


4. Map Data

After simulation, apply the mapping strategy chosen in step 2. For the two-layer chained approach:

// Chain 1: Controller data → simulation steps (via FileNo/LineNo → ActualTime)
MapSingleByCsvFile("Data/controller.csv");

// Chain 2: Sensor data → simulation steps (via ActualTime → sensor readings)
MapSeriesByCsvFile("Data/sensor.csv");

The chaining works because:

  • Simulation steps and controller data share FileNo/LineNo anchors
  • Controller data and sensor data share ActualTime anchors
  • After chaining, simulation steps can index sensor data

The second call walks the steps the first one stamped and pairs each with the samples of one cycle after its end — a spindle revolution under the default SpindleCycle sampling mode, one flute's arc under FluteCycle. A step whose window holds no sample — outside the recording, or inside a dropout — is left unmapped and counted once in the Map-ShotGap--StepsSkipped warning.

Tip

Due to machine acceleration/deceleration, simulation time and actual time diverge over longer durations. Anchor-based linear projection corrects for this drift.


5. View Mapped Results

After mapping, each step carries its samples. In the web application they show in three places — the sensor cycle charts of the selected step, the step properties derived from the samples, and the run's messages — set out in From the web application; a mapped step property also colours the workpiece in the 3D scene when it is the Color Index chart's key.

Export mapped results from a script:

WriteStepFiles("Output/[NcName].step.csv");
WriteShotFiles("Output/[NcName].shot.csv", 1);

Complete Two-Layer Mapping Example

// Configure resolution
MachiningResolution_mm = 0.125;
EnablePhysics = true;

// Clear any previous mapping data
ClearTimeMappingData();

// Run simulation using NC interpreter for accurate paths
PlayNcFile("NC/machining.nc");

// Map controller data (contains FileNo, LineNo, ActualTime)
MapSingleByCsvFile("Data/controller.csv");

// Map high-rate sensor data (contains ActualTime and force/torque)
MapSeriesByCsvFile("Data/sensor.csv");

// Export results
WriteStepFiles("Output/[NcName].step.csv");

From the web application

The two file mappings are mission commands of their own: Map Single By CSV File and Map Series By CSV File, in the Mapping category of Add Command (The Other Commands). Each carries its file and that file's column titles, and the sensor one the cycle sampling mode. The other calls above go into Script commands, evaluated against the running session like any mission script (The Other Commands). The files live in the project folder, uploaded through the file routes (Files and Named Roots).

The shape is the two mapping commands after the Program File, in the same List:

▸ Mapping
      Script [clear]            ClearTimeMappingData();
      Program File              NC/machining.nc
      Map Single By CSV File    Mapping/controller.csv
      Map Series By CSV File    Mapping/sensor.csv

The session shell is one object for the whole run and every command is evaluated against it in order, so this list is the same thing as the complete example written into one Script command with PlayNcFile in the middle. The order matters the same way: the controller file gives the steps their actual times, and the sensor file is paired through those times. Strategy D is a Script with the AddTimeDataByFile calls before the Program File and no mapping command after it, the anchors in the program triggering the mapping.

Over HTTP the mission is built one entry at a time (Project Construction §3):

Step Request
Add a mapping entry POST /api/Mission/list-command/entries/{listPath} with {"CommandType": "mapsinglebycsvfile"} or "mapseriesbycsvfile", positioned like the Script entry below.
Set its file and column titles GET /api/Mission/commands/{path}/fields lists the fields — relFile, then one per column title (fileNoTag, actualTimeTag, … / timeTag, mxTag, …), and cycleSamplingMode with its options on the sensor command. PUT /api/Mission/commands/{path}/fields/{key} takes the bare JSON value: "Mapping/controller.csv", "Timestamp", "FluteCycle". A value that is not a member of the option list is refused.
Add a Script entry POST /api/Mission/list-command/entries/{listPath} with {"CommandType": "script", "IsEnabled": true, "InsertIndex": n} — n is the position inside the list, 0 first; left out, the entry goes last. The reply's path addresses the entry from then on; GET /api/Mission/list-command/entries lists the mission with every path.
Write the script PUT /api/Mission/commands/{path}/script with {"ScriptText": "…", "Title": "…"}. The reply carries a contentHash; a later PUT may send it back as ExpectedContentHash, and is then refused with 409 content_hash_mismatch if the script was edited elsewhere in between.
Check it compiles POST /api/Mission/compile-check compiles every enabled Script command and answers isValid and a scripts array with, per script, its path, title, hasErrors and diagnostics (id, severity, message, line, column); POST /api/Mission/commands/{path}/compile-check checks one. A start while an enabled script has an error is refused with code: "script_compile_error" — Replay Acceptance §2.
Save POST /api/Project/save — the script text and title are written into the project file.

Where the result shows:

  1. The sensor cycle charts of the selected step. Select a step — a click on a strip chart or on the tool path, or POST /api/execution/cl-strip/select-step with {"stepIndex": n} — and the Step Info column's Sensor Spindle Moment Cycle plots that step's Mx, My, Mz samples against time over its one-cycle window, Sensor Cutting Force Cycle its Fx, Fy, Fz. Both charts are switched on under Preference › Optional Nodes & Charts and are off by default (Inspecting a Step). Over HTTP the same series is GET /api/execution/cycle-line/sensor-spindle-moment, answering {hasData, ts, xs, ys, zs}, and GET /api/execution/cycle-line/dynamometer-force for the force; hasData false is a step with no samples.
  2. The step properties the samples derive. AvgAbsTorqueByMapping_Nm is the mean absolute Mz of the step's samples and AvgMomentXyByMapping_Nm the mean bending magnitude; AvgAbsTorqueErrorRatioWithMapping and AvgMomentXyErrorRatioWithMapping are (simulated − measured) / measured, empty where the measured value is below 0.1 N·m. Pick them in the Step Properties chooser, or plot one along the run in the Color Index chart's property picker (Watching the Run); over HTTP GET /api/execution/step-properties lists the keys, and the strip-chart request of Replay Acceptance §4 returns the series for any of them. These four are physics keys: they are listed and plotted only while physics is enabled for the run (the mission's Enable Physics setting).
  3. The messages, in the shell list of GET /api/Execution/messages. CsvEmbed--Begin and CsvEmbed--Done bracket the controller layer. Map-ShotGap--StepsSkipped is a warning counting the steps whose one-cycle window held no sample: the steps outside the recording, which a recording that does not cover the whole program produces as a matter of course, plus any dropout inside it. TimeShotFile--NotFound is a missing sensor file, and TimeShotFile--ReadFailed a sensor file whose header row lacks the time column or a column title the command names, with the missing title in the message. A controller file that cannot be read, or lacks its file-number, line-number or time column, has no message of its own; it faults whatever called the mapping. From a mapping command that is Map-SingleCsv--Fault (Map-SeriesCsv--Fault for the sensor file, after its TimeShotFile--ReadFailed), from a script ScriptCommand-Run--Fault — each with the missing header named, and each leaving the mission to go on to its next entry.

When there is no controller log

The first layer of the chain exists to give each step a real-clock time. When the controller wrote no log, the simulation's own timeline stands in for it: every step already carries FileNo, LineNo and its simulated end time, and all a controller table has to add is the offset between the simulation clock and the recording's clock.

Take the timeline from the run. The step file that Post-Execution's Step Files Output or WriteStepFiles writes has the columns — FileNo, LineNo, EndTimecode, and an ActualTimecode written as null on an unmapped run — and the controller reader takes it as it is, falling back to EndTimecode when the actual-time cells do not parse. For a job with subprograms take the timeline from the strip-chart request below rather than the step files: the step writer reopens a program's file each time the play returns to it, so a main program that calls a subprogram keeps only the rows after the return. Over HTTP, GET /api/execution/strip-chart?aspect=Individual&inspectingKey=FileNo&xValueCategory=IndexByTime&widthHint=0&dispBegin=0&dispEnd=<count> returns the end time of every step in xs and its FileNo in items[0].max; the same request with inspectingKey=LineNo gives the line. Each key is posted first as the project's inspecting key (POST /api/execution/inspecting-key, Replay Acceptance §2): the answer says pending: true until the host has filled that key's values, and a key that is not the project's answers empty items; post the key the project held before back when done, while the project's key is still the one you posted.

Add the offset. Write ActualTimecode as EndTimecode + offset on every row, as a timecode (hh:mm:ss.fff), and keep FileNo and LineNo. The offset is the recording's clock reading at simulated time zero: a time of day when the recorder writes its clock, seconds when it counts from its own start.

Or shift the recording instead of the table. When the recording's time column carries absolute date-times, no table has to be written at all. Leave the step file as it is — the controller reader gives each step its simulated EndTimecode — and set the mapping anchor to the recording's clock reading at simulated time zero before mapping; the sensor reader turns every absolute sample time into its distance from the anchor, which then lands on the simulated clock:

MappingAnchorDateTime = new DateTime(2025, 3, 4, 10, 0, 3, 750);  // recording clock at simulated zero
MapSingleByCsvFile("Output/finishing.nc.step.csv");               // the program's own step file
MapSeriesByCsvFile("Mapping/sensor.csv");

Two conditions come with it. The step file has to come from a play whose ActualTimecode column is null or already holds simulated timecodes: the controller reader prefers that column when it parses, and a step file written after a mapping that stamped wall-clock times gives every step a time outside the recording — the map then covers nothing, and says so only through Map-ShotGap--StepsSkipped. And the first play of a new mission has no step file yet, since Post-Execution writes it after the mapping script: play once with the mapping script switched off. A subprogram played once keeps a complete step file of its own, which is the one to name; a subprogram called several times keeps only its last call.

Find the offset by correlation, not from one peak. Put a simulated series and a measured one on the same time grid — the simulated torque AvgAbsTorque_Nm over IndexByTime from the strip chart or a step file, and the measured Mz — reduce each to its envelope (the largest magnitude in each grid cell), and cross-correlate the two records whole; the lag at the maximum is the offset. A single peak is not enough: a finishing path repeats every few seconds, and one peak matches every repetition equally well, so a peak-to-peak alignment can land a whole pass early or late. Then run the same correlation on the first half of the record and on the second half separately. The two lags must agree; a lag that grows along the record means the machine's real timing and the simulated one diverge (acceleration, dwells) and one offset cannot map the whole program: fit an offset per stretch of the record and write each stretch's rows with its own, or use a real log.

A recording that stops partway maps as far as it goes: the steps after its end have no samples in their windows and are reported once, with their count, as Map-ShotGap--StepsSkipped. That count is the check — it should be the steps after the recording's end, not more.

Ask the vendor before trusting the file: the time base — which clock stamps the samples, at what rate, and whether it is the controller's clock or the sensor's own; the calibration — the factor from the file's numbers to N·m on each channel, and which channel is the torque about the tool axis; and the reference point of the bending channels — the height on the tool axis they are referred to, which is what the tool's observation point must be set to (Smart Tool Holder).

A vendor file

A sensor file rarely arrives with the headers of §1, and it does not have to: the mapping command (or the call's configuration, §1 Column Titles Are Configurable) names the vendor's own titles, so the file is mapped as it came. The substance is deciding which of the vendor's columns can honestly be named as the channels the reader gives them.

  • Time. Name the vendor's time column as the Time Column (TimeTag); its cells may be seconds, hh:mm:ss.fff, or a date-bearing instant, as §1 says. Keep whatever clock the vendor recorded — the controller table absorbs the offset.
  • Torque. The axial torque about the tool axis is the Moment Z Column (MzTag), in N·m. It is the channel the torque step properties above compare against.
  • Bending. The Moment X / Y Columns are bending moments about the observation point, in N·m. Name the vendor's bending channels there only when the vendor has stated their unit and the point they are referred to: a bending moment of unknown reference height is a scaling error nothing reports, and a raw strain reading is not a moment. Otherwise leave those two tags at their defaults — a channel the file does not carry reads NaN, and the torque still maps. HiNC's Mx is the moment about the X axis (see What Mx, My and Mz Mean on the Smart Tool Holder page); a vendor's “bending X” may be bending toward X, the moment about Y — settle it with the push check before naming the column here. Only Map Series by CSV File reads these tags; AddTimeDataByFile and the online setup-angle estimation read the built-in names (Mx, Holder.Mx, Spindle.Mx, and Time), so a converted copy with those headers works on every path.
  • Everything else — sample counters, status flags, the vendor's own computed columns — is dropped by this reader whatever it is called.
  • A unit is not a title. Naming a column does not convert it: a torque in N·mm, or a force in kN, still has to be rescaled in a converted copy of the file. Keep the vendor's original beside it.
  • A gain from a report is a last resort. When the file carries raw counts and the only calibration to hand is a report's extreme (“peak torque 12 N·m”), dividing the report's extreme by the raw extreme gives a gain that reproduces one number. Use it only when the vendor cannot supply the calibration, and record it in the project's README as an assumption, with the report and the raw extreme it came from, so it is replaced when the calibration arrives (Project Construction §7).

A four-row vendor file — Time in seconds, two bending channels of unstated reference, a torque in N·m:

Time,BendX,BendY,Tq
0.000,-0.031,0.108,0.412
0.001,0.044,-0.045,0.398
0.002,0.012,0.061,0.421
0.003,-0.027,0.019,0.405

maps as it is with one title named — Time is already an accepted time header, and the bending channels stay unnamed until their reference point is known:

MapSeriesByCsvFile("Mapping/vendor.csv", new MapSeriesCsvConfig { MzTag = "Tq" });

On the Map Series By CSV File command that is the Moment Z Column field set to Tq.

See Also