This is awesome, now I need to learn how to import this to my tracker. This will save 7-8 min per car
Just a quick guide on how to set up an Excel sheet so it automatically combines statistics from a bunch of CSVs:
- Read the part of the instructions in the topic that tells you how:
oops…
Big props for making this tool! It’s been a huge help.
The Dumb, Hard Way
-
Create a folder named anything you like.
-
Inside, create an Excel sheet and another folder (again, both named anything you like.) This second folder will hold your CSV files.
-
Put exported CSVs into your CSV folder.
-
Open your Excel sheet. In Excel, navigate to Data > Get Data > From File > From Folder.
-
Find the folder with your CSVs. Select that folder, then click Open.
-
Verify that the files you want are shown on-screen. If so, go to Combine > Combine & Load and click.
-
On the next screen, click OK.
-
Voila! Your sheet now contains all sorts of data.
Optional Steps:
-
Hide or delete columns that you don’t think will be relevant
At present, this sheet probably contains lots of data that you don’t really care about. You can either hide columns by selecting them by clicking and dragging on the column bars, right clicking, and clicking “hide.” You can also delete them altogether the same way.
-
Freeze panes with important information
You can keep important info (like car names and column labels) “pinned” to the top or side of the screen using the Freeze Panes option under View > Freeze Panes > Freeze Panes.
- Figure out which columns and rows you want to “pin” to the top and left sides of the screen. For example, let’s say we want to keep the first two columns and the first row.
- Select the top-rightmost cell that is not inside the area you want to freeze. In this case, that’s the cell in the third column and second row.
- Click View > Freeze Panes > Freeze Panes.
Voila!
-
Refresh to add new data.
You may want to add in new CSV files later. In that case, go to the Query tab and click Refresh.
The new data should show up in your sheet.
Hope this helps!
Super handy tool, thank you so much for this!!
I did find a bug in the export - the Entertainment column listed Standard CD as Luxury CD - all those in green are actually Standard CD in automation.
Not sure if the right place to report it.
A new version of the exporter is now available
The release files have now been moved to GitHub releases
Source code is now available on GitHub as well to allow community contributions
Really glad to see this all open sourced! Should allow for more development of user exporters, between this and the SDK provided by Camshaft (which, I’ll admit, I never saw).
like karhgath said, not sure if this is a good place to paste bug reports - but the Model Name column currently exports the trim name. Also, is there any way we can get the stress thresholds for pistons, or are we stuck with the “engine explodes after one crank” thresholds?
Small bug report: The exporter throws errors if the car name ends with a space
Version 5 released
- Added export parameter saving
- Added car cost approximation with production presets
- Fixed issues caused by car names ending with whitespaces
Version 5 update released!
- Added preset choice saving
- Fixed checkbox saving working incorrectly
- Improved whitespace handling
Legacy Post
Go to newest versionExport your cars to CSV files!
The all new custom exporter allows you to export your beloved cars to a common CSV file!
Usage
If you have already used the BeamNG exporter the process should be familiar to you.
Select your car and go to the exporting page, select "csvExporter" in the top right corner, configure it to your likings and press export
The result will be exported to your Automation user directory, after the export a finished you will be prompted with an option to view the directory
There are several options you can configure in the exporter screen:
- Car Name - The name of your car that will be displayed in the exported file(s)
- File Name - Name of the file the data will be exported to. You can export several cars into the same file, to do that simply set this file name the same for all cars you wish to combine
- Delimiter - The character(s) used to separate the keys and values. The default is “,” (comma)
- Export Choices And Results Separately - If ticked, will export the engineering choices and the stats into separate files
- Export Car and Engine Separately - If ticked, will export the car and the engine into separate files. This can be combined with the previous option to export 4 separate files
- Cost preset - Selects a particular preset for calculating costs for the trim, the engine and the car as a whole, the input value must be a number from 0 to 13 (more information about presets below)
Cost calculator presets
Currently there are 14 presets in the cost calculator, ranging from 0 to 13
- Medium factory, cheap labor, medium automation, 2 shifts
- Large factory, cheap labor, medium automation, 2 shifts
- Medium factory, average labor, high automation, 2 shifts
- Medium factory, average labor, high automation, 3 shifts
- Small factory, average labor, medium automation, 2 shifts
- Small factory, cheap labor, medium automation, 3 shifts
- Tiny factory, cheap labor, no automation, 2 shifts
- Tiny factory, average labor, no automation, 2 shifts
- Tiny factory, expensive labor, no automation, 1 shift
- Medium factory, very cheap labor, low automation, 3 shifts
- Large factory, very cheap labor, low automation, 3 shifts
- Large factory, average labor, medium automation, 2 shifts
- Small factory, expensive labor, high automation, 2 shifts
- Tiny factory, expensive labor, high automation, 2 shifts
Installation
- Download CSVExporter
- Navigate to the Steam directory where your Automation is installed
- Navigate further to Steam\steamapps\common\Automation\UE427\AutomationGame\Content\ExportPlugins
- Put the DLL files into this directory
If you would like to contribute to the development of csvExporter you can do so on the GitHub repository
Hello! Sorry but I do not understand what the export is for, we can see more details of the car or something? Thank you!
It also looks something fun to do, I would like to learn hahaha
There’s very, very little (if anything) that this exporter exposes which isn’t already in the game.
What it does do, however, is collate the information for a bunch of cars in a spreadsheet incredibly easily. If you need to look at the stats of ten, twenty, fifty cars… this automatically puts them in a spreadsheet for you. You may never have needed to look at the stats of that many cars, but people hosting challenges here on the forum absolutely do. This cuts data entry which could take an entire day down to maybe an hour on the high side.
This is especially true for challenges with high turnouts (20 or more entrants), but it’s still useful even when there’s a small entry list of 10 or fewer entrants.
Thank you very much, it looks really useful for the forum and the challenges as well as for our own car brands, I’m going to try it!
Not sure if I did something wrong, but running the latest version of CSVExporter2, the car files generated are not compatible with the current version of the game for some reason.
Steps to reproduce:
- Export a car with CSVExporter2
- Copy the .car file from the output to CarSaveImport
- Receive an incompatible version error on attempting to import again
I can’t get the exporter plugin to show up in the dropdown menu (it only contains the BeamNG exporter).
Since @AMuteCrypt has a more “late stage” issue, I’m assuming it’s a local/user error issue on my part, and the exporter works fine for others?
I followed the readme instructions, and deleted the old version.
The CSVExporter “1” worked perfectly for me last year BTW.
EXTENDING CSV EXPORTER: A HOW-TO GUIDE
Good news: CSV Exporter allows you to use your own exporter script!
It’s not super well-documented though. You have a sample Lua script, JSON file and such, but that’s really it. So, let’s go through and document it a little, shall we? This post will assume some general familiarity with programming languages, general concepts and such. Let’s get into it.
PART ONE: THE SAMPLE LUA SCRIPT
Let’s start with how the sample script works, shall we? For what it’s worth, I haven’t fully worked out the boilerplate. Why not? Because Endfinity didn’t comment any of the source code and I don’t wanna wade through all of the assorted uncommented C++ source. Well, not more than I already did to find out where the files need to be. Maybe another time.
Anyway, here’s the file if you wanna refer to it rather than downloading and looking at it locally. So, the file has three functions and some code. The function we really care about is…
CExporter.ExportCarData(CarCalculator)
This single function is the one most responsible for turning Automation’s internal data structures into something that the script and such can handle. It’s also the only part of the Lua script you need to modify. So, let’s look at the beginning of the function, shall we?
local CarInfo = CarCalculator.CarInfo
local EngineInfo = CarInfo.TrimInfo.EngineInfo
local GearboxInfo = CarInfo.TrimInfo.Gearbox
local Results = CarInfo.TrimInfo.Results
local SuspensionDetails = CarInfo.TrimInfo.SuspensionDetails
local carParameters = CarCalculator:GetCarParameters()
local Data = {}
So, what does this do? Well, it… Loads a bunch of information from CarCalculator and unpacks them all. But what are all of these things it refers to?
Well… If you open the game, open a car and hit F9, it becomes pretty obvious. Open the Lua tab, open the Lua Car option, then you see one labelled CarInfo (oh boy!) which has something labelled TrimInfo (oh boy!) which contains entries such as EngineInfo, Gearbox, Results (oh boy!)
Yep, it’s loading the data you see in the F9 menu into a Lua script. That makes things nice and easy for us. What about that last line? Well, braces are used to create tables in Lua - which work similarly to dicts in other languages. Scrolling down, we get a lot of lines like this:
Data.ChassisType = CarInfo.PlatformInfo.Chassis.Name
Data.ChassisMat = CarInfo.PlatformInfo.ChassisMaterial.Name
Data.PanelMat = CarInfo.PlatformInfo.PanelMaterial.Name
Data.SuspensionFront = CarCalculator.CarInfo.PlatformInfo.FrontSuspension.Name
Data.SuspensionRear = CarCalculator.CarInfo.PlatformInfo.RearSuspension.Name
So, this populates a bunch of fields into that Data object - so Data.ChassisType is populated with whatever the F9 info says the Chassis’s name is. That might be something like Chassis_LightTruckMonocoque_Name - that’s the chassis currently known as “Partial Monocoque”, which used to be known as “Light Truck Monocoque”. It just keeps going on down the list like that, and a lot of the script is ultimately busywork like this - take a value from the internals, copy it to Data under some other name. We’ll get down to why it went where it did a little later. Okay, what about lines like this?
--Data.BodyHardtop = CarInfo.TrimInfo.Body.ConvertableHard
--Data.BodySofttop = CarInfo.TrimInfo.Body.ConvertableSoft
Well, lines starting with two dashes are comments in Lua, so these lines do nothing.
Let’s get to the actually interesting parts, shall we?
--for i, v in ipairs(allEmissionCycles) do
-- local tp = type(v)
-- local vd = dump(v)
-- local br = tp + vd
-- if v.Passrate >= 100 then
-- wesLevel = math.max(wesLevel, i)
-- end
--end
This is commented out (because it stopped being the good way to do this, it can be directly accessed) but what did it do before? Simple, it iterated through the possible test levels and such, checked to see if that level had passed, and stored it. There’s a slight potential error around WES9 but this is a good illustrative example of how we can do some more complex maths right here. We can put any Lua code we want in here, in fact! It’ll run, we just need to write stuff to Data.
Which is where the example right underneath it comes in.
130 lines of code
local function CalculateCost(engineeringTime, productionUnits, engineeringCost, materialCost, toolingCosts,
employeeCount, employeeWage, automationCoef, shiftCount)
local employeeCostsPerShift = employeeCount * employeeWage * 8
local factoryProductionUnits = productionUnits / (employeeCount * automationCoef)
local carsMadePerShift = 8 / factoryProductionUnits
local carsMadePerDay = carsMadePerShift * shiftCount
local employeeCostsPerDay = employeeCostsPerShift * shiftCount
local carsMadePerMonth = carsMadePerDay * 30
local employeeCostsPerMonth = employeeCostsPerDay * 30
local employeeCostsPerCar = employeeCostsPerShift / carsMadePerShift
local monthlyEngineeringCosts = engineeringCost / 60
local engineeringCostsPerCar = monthlyEngineeringCosts / carsMadePerMonth
local toolingCosts = toolingCosts * (shiftCount / 2)
local totalCostPerCar = materialCost + engineeringCostsPerCar + employeeCostsPerCar + toolingCosts
return totalCostPerCar
end --CalculateCost
--Data.EngineEngineeringTime
--Data.EngineProductionUnits
--Data.EngineEngineeringCost
--Data.EngineMaterialCost
--Data.EngineToolingCosts
--Data.TrimEngineeringTime
--Data.TrimManHours
--Data.TrimEngCosts
--Data.MatCost
--Data.ToolingCosts
--Medium factory, cheap labor, medium automation, 2 shifts
Data.TrimCostPreset0 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
200, 10, 1.5, 2)
Data.EngineCostPreset0 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
200, 10, 1.5, 2)
Data.CarCostPreset0 = Data.TrimCostPreset0 + Data.EngineCostPreset0
--Large factory, cheap labor, medium automation, 2 shifts
Data.TrimCostPreset1 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
500, 10, 1.5, 2)
Data.EngineCostPreset1 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
500, 10, 1.5, 2)
Data.CarCostPreset1 = Data.TrimCostPreset1 + Data.EngineCostPreset1
--Medium factory, average labor, high automation, 2 shifts
Data.TrimCostPreset2 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
150, 20, 2, 2)
Data.EngineCostPreset2 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
150, 20, 2, 2)
Data.CarCostPreset2 = Data.TrimCostPreset2 + Data.EngineCostPreset2
--Medium factory, average labor, high automation, 3 shifts
Data.TrimCostPreset3 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
100, 20, 2, 3)
Data.EngineCostPreset3 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
100, 20, 2, 3)
Data.CarCostPreset3 = Data.TrimCostPreset3 + Data.EngineCostPreset3
--Small factory, average labor, medium automation, 2 shifts
Data.TrimCostPreset4 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
50, 20, 1.5, 2)
Data.EngineCostPreset4 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
50, 20, 1.5, 2)
Data.CarCostPreset4 = Data.TrimCostPreset4 + Data.EngineCostPreset4
--Small factory, cheap labor, medium automation, 3 shifts
Data.TrimCostPreset5 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
50, 10, 1.5, 3)
Data.EngineCostPreset5 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
50, 10, 1.5, 3)
Data.CarCostPreset5 = Data.TrimCostPreset5 + Data.EngineCostPreset5
--Tiny factory, cheap labor, no automation, 2 shifts
Data.TrimCostPreset6 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
10, 10, 1, 2)
Data.EngineCostPreset6 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
10, 10, 1, 2)
Data.CarCostPreset6 = Data.TrimCostPreset6 + Data.EngineCostPreset6
--Tiny factory, average labor, no automation, 2 shifts
Data.TrimCostPreset7 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
10, 20, 1, 2)
Data.EngineCostPreset7 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
10, 20, 1, 2)
Data.CarCostPreset7 = Data.TrimCostPreset7 + Data.EngineCostPreset7
--Tiny factory, expensive labor, no automation, 1 shift
Data.TrimCostPreset8 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
10, 30, 1, 1)
Data.EngineCostPreset8 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
10, 30, 1, 1)
Data.CarCostPreset8 = Data.TrimCostPreset8 + Data.EngineCostPreset8
--Medium factory, very cheap labor, low automation, 3 shifts
Data.TrimCostPreset9 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
200, 5, 1.2, 3)
Data.EngineCostPreset9 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
200, 5, 1.2, 3)
Data.CarCostPreset9 = Data.TrimCostPreset9 + Data.EngineCostPreset9
--Large factory, very cheap labor, low automation, 3 shifts
Data.TrimCostPreset10 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
500, 5, 1.2, 3)
Data.EngineCostPreset10 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
500, 5, 1.2, 3)
Data.CarCostPreset10 = Data.TrimCostPreset10 + Data.EngineCostPreset10
--Large factory, average labor, medium automation, 2 shifts
Data.TrimCostPreset11 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
500, 20, 1.5, 2)
Data.EngineCostPreset11 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
500, 20, 1.5, 2)
Data.CarCostPreset11 = Data.TrimCostPreset11 + Data.EngineCostPreset11
--Small factory, expensive labor, high automation, 2 shifts
Data.TrimCostPreset12 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
50, 30, 2, 2)
Data.EngineCostPreset12 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
50, 30, 2, 2)
Data.CarCostPreset12 = Data.TrimCostPreset12 + Data.EngineCostPreset12
--Tiny factory, expensive labor, high automation, 2 shifts
Data.TrimCostPreset13 = CalculateCost(Data.TrimEngineeringTime, Data.TrimManHours, Data.TrimEngCosts, Data.MatCost, Data.ToolingCosts,
10, 30, 2, 2)
Data.EngineCostPreset13 = CalculateCost(Data.EngineEngineeringTime, Data.EngineProductionUnits, Data.EngineEngineeringCost, Data.EngineMaterialCost, Data.EngineToolingCosts,
10, 30, 2, 2)
Data.CarCostPreset13 = Data.TrimCostPreset13 + Data.EngineCostPreset13
return Data
So, what does this long example does? Well, it defines a function inside the ExportCarData function, then uses that a bunch of times. We also get the one example of comments here, examples of what different presets mean. This is really, really useful for us, for two reasons. One, it shows us how we can define a function and reuse it. Two, it shows us how the JSON is useful.
PART TWO: THE JSON FILE
So, we have a Data object filled with many, many fields… What happens with it? It goes back up through the boilerplate and into the innards of the exporter DLL and I don’t need to care about it, because I can look at the JSON and work it all out.
Looking at the “Dataset” JSON file, early on, we get something familiar:
{"ModelName": {"Translation": "Model Name", "DataType": "DataType_String"}},
{"ModelYear": {"Translation": "Model Year", "DataType": "DataType_Float"}},
{"BodyWheelbase": {"Translation": "Wheelbase", "DataType": "DataType_Float"}},
{"ChassisType": {"Translation": "Chassis Type", "DataType": "DataType_String"}},
{"ChassisMat": {"Translation": "Chassis Material", "DataType": "DataType_String"}},
Just a few lines in, and we already start to get the names that were used before - ChassisType, ChassisMat, stuff like that. There’s some that were added on elsewhere, but this is nice and useful, this is just what we want. In fact, the items listed in the JSON and not commented out are all of the fields we get in an export result - and extra fields, like CarCostPreset13? They’re in the Data object from the Lua script, but they’re not in the JSON, so they don’t go into the export.
There’s a second JSON, the Translations file. Whenever a string would be output, it’s first checked in the Translations file. If the string has a field in the JSON, it’s replaced. For instance, there’s a line in the translations file that says
"Piston_LowFCast_Name": "Low-Friction Cast",
So this turns an internal name into a readable one. Unfortunately, it’s followed by…
"R": "Rear",
… which turns the tyre index R to Rear
PART THREE: PUTTING IT ALL TOGETHER
So, what do we do to create our own custom exporter script? Just follow these simple steps:
- Make a new copy of the JSON and Lua files somewhere.
- Use the F9 menu to find out where the information you want is stored. Want to find out how much a car’s offroad is being changed due to braking at low load? That’s stored under Results.OffroadFactors.factorBrakeReserves
- Add some code to the new copy of the Lua script to add a new field into the Data object, something like Data.OffroadFactorBrakeReserves = Results.OffroadFactors.factorBrakeReserves. This can be simple or complex.
- Add a line to the Dataset JSON which includes the name you used internally, the name you want to use externally and the appropriate data type, something like {“OffroadFactorBrakeReserves”: {“Translation”: “Offroad Brake Reserve Factor”, “DataType”: “DataType_Float”}}
- Place your new files in %LocalAppData%\AutomationGame\CSVExporter 2\UserFiles
- Set the “Lua File” parameter to the file name you picked for the Lua file. Do the same for the Dataset file, and so on.
Exports.zip (5.4 KB)
Here’s an example - placing the Lua file and the JSON dataset into the UserFiles folder and running CSVExporter with these files will generate something like the CSV file included in that .zip.