Saving streamed Data from Unity in a csv File - c#

I have to record the coordinate-data (vector of x,y,z) of an eye-tracking System and save it, for later evaluation. The whole eye-tracking System is integrated inside a Head-mounted-Display and the software runs over Unity.
After some research, I figured out that saving the Data in a CSV file would probably the easiest way. This is what I got so far:
void update()
{
string filePath = #"C:\Data.csv";
string delimiter = ",";
Vector3 leftGazeDirection = smiInstance.smi_GetLeftGazeDirection();
Vector3 rightGazeDirection = smiInstance.smi_GetRightGazeDirection();
float[][] output = new float[][]{
new float[]{leftGazeDirection.x},
new float[]{leftGazeDirection.y},
new float[]{leftGazeDirection.z},
new float[]{rightGazeDirection.x},
new float[]{rightGazeDirection.y},
new float[]{rightGazeDirection.z} };
int length = output.GetLength(0);
StringBuilder sb = new StringBuilder();
for (int index = 0; index < length; index++)
sb.AppendLine(string.Join(delimiter, output[index]));
File.WriteAllText(#"C:\Data.csv", sb.ToString());
}
What this gives me out is a CSV file with the Vector of the latest Position of the Gazedirection. What I need would be a Record of all the Gazedirections that were made in one Session. Is it possible to get something like this?
Can I somehow modify my Code to achieve this or should I try something completely different?
Since I'm very newbie to unity and programming in general I just have a lack of vocabulary and don't know what to search for to solve my problem..
I would be very thankful if somebody could help me. :)

Welcome to StackOverflow. A good question, and well set out.
Presumably after you save this data away, you want to do something with it. I would suggest that your life is going to be a lot easier if you were to create a database to store your data. There are tons of tutorials on this sort of thing, and since you are already writing in C# it should not be too hard for you.
I would be creating a SQL Server database - either the Express version or Developer Version, both would be free for you.
I would steer away from trying Entity Framework or similar at this stage, I would just use the basic SQLClient to connect and write to your database.
Once you start using this then adding something like a Session column to separate one session from the next becomes easy, and all sorts of analysis you might want to do onthe data will also become much easier.
Hope this helps, and good luck with your project.

Yes. You can have data for one entire session. Look no further than File.AppendAllText. In this scenario I'm assuming that you have 6 values for two gaze pointers. In that case you don't need to write it down as multidimensional array as it is just wasting allocated memory.
Here we can proceed to save it as 6 values for each iteration of your loop.
string filePath = #"C:\Data.csv";
string delimiter = ",";
void Start()
{
if(File.Exists(filePath))
File.Delete(filePath);
}
void Update
{
Vector3 leftGazeDirection = smiInstance.smi_GetLeftGazeDirection();
Vector3 rightGazeDirection = smiInstance.smi_GetRightGazeDirection();
float[] output = new float[]{
leftGazeDirection.x,
leftGazeDirection.y,
leftGazeDirection.z,
rightGazeDirection.x,
rightGazeDirection.y,
rightGazeDirection.z };
int length = output.Length;
StringBuilder sb = new StringBuilder();
for (int index = 0; index < length; index++)
sb.AppendLine(output[index],delimiter));
if(!File.Exists(filePath))
File.WriteAllText(filePath, sb.ToString());
else
File.AppendAllText(filePath, sb.ToString());
}
AppendAllText will keep on appending file till the end of execution. Note that this solution has downside that is at the start we will delete the file for each session so you will need to manually keep track of each session.
So if you want to keep a bunch of files related to each session and not to overrride the same file we can include the date and time stamp while creating each file. So instead of deleting old file for each new session we are creating file for each session and writing in it. Only start method will need to change to handle datetime stamp for file name appending. Rest of the Update loop will be same.
string filePath = #"C:\Data";
string delimiter = ",";
void Start()
{
filePath = filePath + DateTime.Now.ToString("yyyy-mm-dd-hh-mm-ss") + ".csv";
}

Related

Client side Blazor: Slow load of a resource

I have some lines of code, that worked well in Xamarin.Forms, but it performs very poorly in Client-side Blazor.
This is the code:
string s = System.Text.Encoding.UTF8.GetString(Properties.Resources.city_list);
List _cityList = JsonConvert.DeserializeObject<List>(s).ToList();
the city_list is a huge list from OpenWeatherMap that is containing all the cities around the globe.
Later I want to display this list as options in a select, so I would like to keep it on the client-side. The code now is very slow, it takes minutes to run it. Do you have an idea, how can I make it faster?
Thank you in advance
Janos
Update:
I removed the Take(20), because that is not part of the problem. I want to get the full list.
The city_list is a text file in Json format. I added it as Resource, so it is a byte array in this code.
This problem is related to very slow string parsing in Blazor. Yesterday I hit the same problem with parsing a json data of 6 mb. Even with the small json data the delays between page landings are noticable to eye. In a non-Blazor .NET application parsing is done really fast though.
So I figured out whether the problem is about json parsing or in general string parsing. I tested the below code on a reguler Console app and on Blazor WebAssembly. The difference is huge: Console(768 milliseconds), Blazor 45(seconds). Seems like this is an issue with web assembly because javascript parsing is also fast. This problem has been around for sometime. See also here.
https://github.com/dotnet/runtime/issues/40386
Well this being said even though this is a very simple problem it completely invalidates Blazor as a production ready framework.
This was tested on .NET 6 both for Console and Blazor.
public class StringParsePerfTest
{
private string Text { get; set; } = "";
public StringParsePerfTest()
{
for(int i = 0; i < 10000; i++)
{
if (i % 3 == 0) Text += "|";
else Text += "a";
}
}
public void Test()
{
var start = DateTime.Now;
start= DateTime.Now;
for(int i = 0; i < 10000; i++)
{
var splitted = Text.Split("|");
}
var end = DateTime.Now;
Console.WriteLine($"{(end - start).TotalMilliseconds}");
}
}

C# - Saving and Loading data to file

I decided to get into coding and am learning c#, after making a few small projects, I decided to step it up a little and make a text adventure game, with saving and loading, and if I get to feeling zany I'll try to add some multiplayer. While I haven't really hit a road block because of it, I can't help but feel that I am doing load function REALLY sub-optimally. The save is fine, I feel like it works for me, but the load I feel can be really simplified, I just don't know what to use.
I also wouldn't really mind, but with this way, if I add other attributes/skills or whatever else that needs to be saved, I will have to add everything to the load function as well, and it will be even longer.
I have tried to search around on here, the c# documentation, and other sites, but can't find a solution that works for this case. can anyone help me find a better way of doing this? Or is this the best I can really do since it's varying data types?
Edit: To simplify and clarify what answer I am searching for, I am trying to find a simpler and more scalable way to save and load the data to a file.
static void LoadGame(CharData PlayerData)
{
Console.WriteLine("Enter the name of the character to load as shown below.");
//getting current directory info, setting to di
DirectoryInfo di = new DirectoryInfo(Directory.GetCurrentDirectory());
//need to initialize these outside of a loop
int SaveFiles = 0;
string DisplayName = " ";
int DisplayNameLength = 0;
//looks through files in working directory ending in '.fasv', displays them in format '{x}. John Smith'
foreach (var fi in di.GetFiles("*.fasv"))
{
SaveFiles++;
DisplayNameLength = fi.Name.Length;
//remove .fasv from displayed name to make it look nicer
DisplayName = fi.Name.Remove(DisplayNameLength - 5, 5);
Console.WriteLine(SaveFiles.ToString() + ". " + DisplayName);
}
string toLoad = Console.ReadLine();
using StreamReader sr = new StreamReader(toLoad + ".fasv");
//the name is easy to get since it's a string. but integers...
PlayerData.Name = sr.ReadLine();
//... not so much. i hate all of this and i feel like it's gross, but i don't know how else to do it
int hp, xp, level, toughness, innovation, mind, empathy, spryness;
Int32.TryParse(sr.ReadLine(), out hp);
Int32.TryParse(sr.ReadLine(), out xp);
Int32.TryParse(sr.ReadLine(), out level);
Int32.TryParse(sr.ReadLine(), out toughness);
Int32.TryParse(sr.ReadLine(), out innovation);
Int32.TryParse(sr.ReadLine(), out mind);
Int32.TryParse(sr.ReadLine(), out empathy);
Int32.TryParse(sr.ReadLine(), out spryness);
PlayerData.Health = hp;
PlayerData.Level = level;
PlayerData.XP = xp;
PlayerData.Toughness = toughness;
PlayerData.Innovation = innovation;
PlayerData.Mind = mind;
PlayerData.Empathy = empathy;
PlayerData.Spryness = spryness;
sr.Close();
InGame(PlayerData);
}
static void SaveGame(CharData PlayerData)
{
using (StreamWriter sw = new StreamWriter(PlayerData.Name + ".fasv"))
{
foreach (System.Reflection.PropertyInfo stat in PlayerData.GetType().GetProperties())
{
//write player data properties to file line by line, using stat to iterate through the player data properties
sw.WriteLine(stat.GetValue(PlayerData));
}
sw.Close();
}
}
If you aren't set on a particular data format for the file data, I would recommend using a serializer such as JSON.NET. You can use NuGet to add newtonsoft.json to your project, and that would allow you to just do something similar to:
using (StreamWriter file = File.CreateText(pathToPlayerFile))
{
var serializer = new JsonSerializer();
serializer.Serialize(file, playerData);
}
And then your code to read from the file would be pretty similar:
using (var file = File.OpenText(pathToPlayerFile))
{
var serializer = new JsonSerializer();
return (CharData)serializer.Deserialize(file, typeof(CharData));
}
I borrowed those code snippets from newtonsoft.com. CreateText will create (or overwrite) the file and write the object as a JSON object.

Adding fields side by side in word footnote

Hi I am having an issue with my word application. I am trying to add a field side by side in the footnotes. The issue I am having is a merge conflict and I think this is because the range is the same and over writhing the other field. I am trying add them side by side in one line based on the end point of the previous field.
I have tried collapsing the range to the end but I can't get this to work. Any help would be much appreciated as I am newish to using the VSTO tools and tbh I find them not very good.
public static void insertHtmlIntoFootnoteResult (Field field, List<ct>
list)
{
for(var c in ct){
//I am trying to go to the end here
field.Result.Collapse(WdCollapseDirection.wdCollapseEnd);
//How do I create a new field and insert it here based of the the
//last fields ending position?
string guid = Guid.NewGuid().ToString();
var filename = Path.GetTempPath() + "temp" + guid + ".html";
using (StreamWriter s = File.CreateText(filename))
{
s.Write("I am test");
s.Close();
}
field.Result.InsertFile(filename);
File.Delete(filename);
}
} // _insertHtmlIntoRange
Hey so I found an answer eventually using the blog post by flowers
https://gist.github.com/FlorianWolters/6257233
Done by changing the insertempty() method to insert a Word.WdFieldType.wdFieldIf field.
Hope this helps for futre people!

C# - Getting the full picture from WUAPI

I am trying to collect an accurate picture of Windows Updates, specifically KB installations, on a number of different machines. I've tried a number of different pieces of code that I've found scattered about, but I still cannot seem to create an accurate picture of what is installed. By accurate, I mean that whatever I gather seems to be a subset of what is shown when I check the Windows Update History on the machine using the Windows UI! Can't seem to figure this out!
Here are a few things I've tried;
UpdateSession uSession = new UpdateSession();
IUpdateSearcher uSearcher = uSession.CreateUpdateSearcher();
uSearcher.Online = false;
ISearchResult sResult = uSearcher.Search("IsInstalled=1");
foreach (IUpdate update in sResult.Updates)
{
foreach (string kbaid in update.KBArticleIDs)
{
txtAllUpdates.AppendText(kbaid + Environment.NewLine);
}
}
I also tried adding code within this same routine to gather all of the updates within the Bundled Updates field, like so;
foreach (IUpdate update2 in update.BundledUpdates)
{
txtAllUpdates.AppendText("\t--> " + update2.Title + Environment.NewLine);
foreach (string kbaid2 in update2.BundledUpdates)
{
string kbNo = GetKBNo(update2.Title.ToLower());
txtAllUpdates.AppendText("\t\t" + kbNo);
}
}
I also tried looking at the Update History, but that provided me with yet another set of data - still not complete!
UpdateSession updateSession = new UpdateSession();
IUpdateSearcher updateSearcher = updateSession.CreateUpdateSearcher();
int count = updateSearcher.GetTotalHistoryCount();
MessageBox.Show("Total Count = " + count);
IUpdateHistoryEntryCollection history = updateSearcher.QueryHistory(0, count);
for (int i = 0; i < count; ++i)
{
txtAllUpdates.AppendText("\t\t\t" + history[i].Title);
}
I also checked into some code that leverages the registry, but from what I've read, that's not the right way to do things. At this point, I'm performing a number of different queries, searching entries for "KB" references and building a list and removing duplicates, but I'm still not getting the same list I see on the screen! Even if this did work, it can't possibly be the right way to go - I feel like I must be missing something.
Finally, I tried to just get information on when updates were last checked for and installed - even that doesn't match up with what is displayed. I did this with the following code;
var auc = new AutomaticUpdatesClass();
DateTime? lastInstallationSuccessDateUtc = null;
if (auc.Results.LastInstallationSuccessDate is DateTime)
lastInstallationSuccessDateUtc = new DateTime(((DateTime)auc.Results.LastInstallationSuccessDate).Ticks, DateTimeKind.Utc);
DateTime? lastSearchSuccessDateUtc = null;
if (auc.Results.LastSearchSuccessDate is DateTime)
lastSearchSuccessDateUtc = new DateTime(((DateTime)auc.Results.LastSearchSuccessDate).Ticks, DateTimeKind.Utc);
lblInstall.Text += lastInstallationSuccessDateUtc.ToString();
lblSearch.Text += lastSearchSuccessDateUtc.ToString();
Does anyone have some expertise in this area? Really want to get this done right!
Thanks for taking the time to read!
Respectfully,
Marshall
All the various ways to find installed software is incomplete, so I used a variety of ways in Get-KbInstalledUpdate, which I describe as a:
Replacement for Get-Hotfix, Get-Package, searching the registry and searching CIM for updates
Though I haven't tried this way, which is essentially:
$session = New-Object -ComObject "Microsoft.Update.Session"
$updatesearcher = $session.CreateUpdateSearcher()
$count = $updatesearcher.GetTotalHistoryCount()
$updates = $updatesearcher.QueryHistory(0, $count)

Selection.MoveEnd not working while using Word Interop and C# to manipulate Word documents

I am developing a .NET program using VSTO 2010 running .NET 4.0 to find a specific subheading in a set of word documents and copy all content under that subheading (say "Requirements") using Word.Interop. I succeeded by means of a for loop that matched words, using which I search for this word and then the starting word of the next section (say "Functionality").
Now the documents also have a contents page so i found that simple word matching wouldn't do as it would return the first seen occurrence which was definitely in the contents section. So I tried finding the second occurrence an was successful but then realized that it could even be that the word might repeat itself much before the subheading. Hence I resorted to finding the sentence. here I was successful here in finding both the words (I had to modify the search string to "Requirements\r" because thats how it was being read)
Anyhow. The problem i am facing now is that after I get the starting and ending sentences, I selected the entire document and using MoveStart and MoveEnd , i reduced down the selection before copying it and pasting it in another word document,(as i dont know about using Range or Bookmark)
However , while i was successful in moving the start and though the end position was correct, the MoveEnd always moves to some text that is at least 10 sentences beyond the actual. I've been at this for 2 weeks now and any help in this matter would be greatly appreciated. I dont mean any disrepect to all the programmers out there in the world.
I've shown the code I'm using.
The variables used are self explanatory.
//SourceApp and SourceDoc - Word application that reads source of release notes
//DestinationApp and DestinationDoc = Word application that writes into new document
private void btnGenerate_Click(object sender, EventArgs e)
{
int startpos = findpos(SourceDoc, 1, starttext, sentencecount);
int endpos = findpos(SourceDoc, startpos, endtext, sentencecount);
object realstart = startpos - 1; // To retain the subheading
object realend = -(sentencecount - (endpos - 1)); // to subtract the next subheading
SourceDoc.Activate();
SourceDoc.ActiveWindow.Selection.WholeStory();
SourceDoc.ActiveWindow.Selection.MoveStart(WdUnits.wdSentence, realstart);
SourceDoc.ActiveWindow.Selection.MoveEnd(WdUnits.wdSentence, realend); // the problematic bit
SourceDoc.ActiveWindow.Selection.Copy();
IDataObject data = Clipboard.GetDataObject();
string allText = data.GetData(DataFormats.Text).ToString();
DestinationDoc.Activate();
DestinationDoc.ActiveWindow.Selection.WholeStory();
DestinationDoc.ActiveWindow.Selection.Delete();
DestinationDoc.ActiveWindow.Selection.Paste();
DestinationDoc.Save();
((_Application)SourceApp).Quit();
((_Application)DestinationApp).Quit();
textBox1.AppendText(allText);
}
int findpos(Document docx, int startpos, string txt, int sentencecount)
{
int pos = 0;
string text;
for (int i = startpos; i <= sentencecount; i++)
{
text = docx.Sentences[i].Text;
if (string.Equals(text, txt))
{
pos = i;
break;
}
}
return pos;
}
I would also be extremely grateful if there was a way to extract specific subheading only (like 3.1 , 5.2.3 etc.) which is what I'm trying to achieve. The question is just my way of doing things and I'm open to a better way as well.
Many thanks in advance.

Categories