Unity Time for calculating session length - datetime

Is there a way of registering time when you exit a Unity application (i.e. you press play and stop the app) or perhaps when you exit a standalone build?
Using the following simple line of code, I can write to the console when my start button was pressed, so I wonder if I could do the same when the app is quit, so that the difference would be the length of the session?
Debug.Log("Start Button pressed # " + DateTime.Now);

At the End of Application you can Save your Session Time. And In Start Method you can Load it From PlayerPrefsto use it.
Like This :
void OnApplicationQuit()
{
DateTime time = new DateTime();
time.AddSeconds(Time.realtimeSinceStartup);
PlayerPrefs.SetString("SessionTime",time.ToBinary().ToString());
}
void Start()
{
DateTime time = new DateTime();
string timeString = PlayerPrefs.SetString("SessionTime","");
if(timeString != "")
{
Convert.ToInt64(timeString);
time = DateTime.FromBinary(time);
Debug.Log("Previous Session Time : " + time.ToString());
}
}
You can also achieve this by using System.DateTime.Now and storing it in some variable at start. and getting Session Time at End by subtracting saved time from System.DateTime.Now. I hope its Helpful :)

You can use Time.realtimeSinceStartup to get the number of seconds since the application was start. Furthermore, OnApplicationQuit will be called (on any MonoBehaviour that implements it) when the application is closed. I am not completely sure it works in the editor but I think it would.
void OnApplicationQuit() {
Debug.Log(Time.realtimeSinceStartup);
}
I hope that helps!

Related

Deacticate User based on last login date

Scenario: Deactivate the user whose login date is less than 42 from today. I have an user whose last login date is 1/22/2020(US Date format)/22/1/2020 5:12 pm. Here I wrote a batch apex for deactivating. My code has executed successfully and my batch status is completed but the user record is not deactivating.
Here is the code:
global class User_Deactivation implements Database.Batchable<SObject>
{
dateTime dt = date.today()-42;
public String query = 'SELECT Name, LastLoginDate, Id From User WHERE IsActive = true AND LastLoginDate=:dt ';
global Database.querylocator start(Database.BatchableContext bc)
{
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext bc,List<User> scope)
{
List<User> userList = new List<User>();
for(User s:scope)
{
User u =(user)s;
userList.add(u);
}
if(userList.size() > 0)
{
for(User usr : userList)
{
usr.isActive = false;
}
}
update userList;
}
global void finish(Database.BatchableContext bc)
{
AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email
FROM AsyncApexJob
WHERE Id = :BC.getJobId()];
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {a.CreatedBy.Email};
mail.setToAddresses(toAddresses);
mail.setSubject('Apex Job Status: ' + a.Status);
mail.setPlainTextBody('The batch Apex job processed ' + a.TotalJobItems + ' batches with '+ a.NumberOfErrors + ' failures.');
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
}
please help me out on this
Multiple things you can improve here, where do I begin...
Initialisation(?) piece
dateTime dt = date.today()-42;
String query = 'SELECT Name, LastLoginDate, Id From User WHERE IsActive = true AND LastLoginDate=:dt';
Do you need Date or DateTime match? The way you wrote it it'll match only people who logged in exactly at midnight. System.debug(dt); would say 2020-01-23T00:00:00.000Z. It shouldn't be an equals sign, should be "less than" or "less or equal".
Or even better - you can make it bit more clear what you want to do, bit more "semantic" so the poor guy who's going to maintain it can understand it without extra comments. This reads more natural and uses the SOQL date literals, special "constants" to simplify your logic: SELECT Id, LastLoginDate FROM User WHERE isActive = true AND LastLoginDate != LAST_N_DAYS:42
What is this section of code anyway. It's not really static variables, it's not a constructor... I think it'll behave as a constructor. Be very, very careful with constructors for batches. The state of the class at the end of the constructor gets saved (serialised) and restored every time the class is scheduled to run. It's tempting to put some initialisation code into constructor, maybe read some custom settings, precalculate stuff... But then you'll be in for nasty surprise when admin adds new custom setting and the batch doesn't pick it up. In your case it's even worse, I'd suspect it'll serialise the dt and your today() will be frozen in time, not what you expected. To be safe move all initialisation logic to start()
And I'd even say whoever gave you the requirement didn't think it through. When you make new user they get a link they need to click in next 72h. If they didn't do it (maybe it was sent late Friday and they want to login on Monday) - this thing will dutifully kill their access at Friday night without giving them any chance to login. You need some "grace period". Maybe something like WHERE isActive = true AND (LastLoginDate < :x OR (LastLoginDate = null AND CreatedDate < :x))
start()
Queries in strings work and that's how a lot of batch documentation is written but they are poor practice. Where possible use a compiled query, in brackets. You get minimal improvement in execution (precompiled), you get compile-time warnings when you mess up (better than a runtime error which you might not notice if you don't monitor jobs). And most importantly - if somebody wants to delete a field - SF will detect a dependency and stop him/her. Use return Database.getQueryLocator([SELECT ...]); wherever you can.
execute()
Your scope already is a list of users, why do you do extra casts to User? Why do you add them to a helper list? Why 2 loops?
for(User u : scope){
u.isActive = false;
}
update users;
and you're done?
P.S. Why "global" all over the place?

How to set a Job Due Date in Activiti 5.22.0?

I got a reference to a Job object like this:
Job timer = managementService.createJobQuery().processInstanceId(execution.getParentId()).singleResult();
Could anyone please tell me how can I set the timer due date to an arbitrary
date or time period in Activiti 5.22.0?
I could not find a suitable method in ManagementService or Job class.
Best regards.
For me, I found only one way to do this:
Create a new timer and set config from the old one.
This is a piece example of how I did this, do not forget to fix it, to avoid NPE
TimerEntity oldTimer = (TimerEntity) managementService.createJobQuery()
.processInstanceId("YOUR_PROCESS_INSTANCE_ID")
.timers()
.singleResult();
commandExecutor.execute(new Command<Void>() {
#Override
public Void execute(CommandContext commandContext) {
oldTimer.delete();
TimerEntity newTimer = new TimerEntity();
newTimer.setJobHandlerConfiguration(oldTimer.getJobHandlerConfiguration());
newTimer.setJobHandlerType(oldTimer.getJobHandlerType());
newTimer.setExclusive(oldTimer.isExclusive());
newTimer.setRepeat(oldTimer.getRepeat());
newTimer.setRetries(oldTimer.getRetries());
newTimer.setEndDate(oldTimer.getEndDate());
newTimer.setExecutionId(oldTimer.getExecutionId());
newTimer.setProcessInstanceId(oldTimer.getProcessInstanceId());
newTimer.setProcessDefinitionId(oldTimer.getProcessDefinitionId());
newTimer.setTenantId(oldTimer.getTenantId());
// Sets a new date
newTimer.setDuedate(new Date());
commandContext.getJobEntityManager().schedule(newTimer);
return null;
}
});

In Unity is a WWW class asynchronous or synchronous?

I am trying to understand how to write a WWW call in Unity. According to the description here http://docs.unity3d.com/ScriptReference/WWW.html I can inspect the isDone property, but in the example on the same page, it makes no attempt to inspect isDone.
The question I have is, if I make a WWW call and it takes several seconds to reply, doesn't the game freeze?
I would like to think the right code is this, but is it?
StartCoroutine(WaitForResponse(myWWWObject));
private IEnumerator WaitForResponse(WWW aRequest ) {
while ( ! aRequest.isDone )
yield return aRequest;
}
Does the game freeze until aRequest is done? Or is it truly asynchronous?
You need to understand Coroutines - a fundamental feature of Unity that allows you to write long-duration code functions (eg: longer then a frame) that will not freeze your game.
http://docs.unity3d.com/Manual/Coroutines.html
In C# you can spot a coroutine function because it has a return type of IEnumerator. And you can spot the locations in the code where the function will suspend and continue from again by the C# keyword yield.
Each MonoBehaviour class can manage coroutines, and if you tell it to start one with StartCoroutine(), then MonoBehaviour will call the coroutine every frame (sometimes more then once) until the Coroutine reaches the end.
For WWW class (which supports co-routines), all you need is to call this:
WWW www = new WWW(url);
yield return www;
You create a WWW class with the URL to retrievve, and the yield will basically be called each frame automatically by the MonoDevelop coroutine manager until www object says it has completed (successfully or failure).
Your game will not freeze at all during this time.
In the linked documentation page for WWW, the following method is a coroutine method:
IEnumerator Start() {
WWW www = new WWW(url);
yield return www;
Renderer renderer = GetComponent<Renderer>();
renderer.material.mainTexture = www.texture;
}
The yield statement is used to pause execution and return control to Unity and then to resume execution in the next game frame. For example yield return null; will cause execution to resume in the next frame. Additionally, you can extend that behaviour by using one of the classes that derive from YieldInstruction class, for example WaitForSeconds.
Consider yield return new WaitForSeconds(1f); - this will resume execution after 1 second of game time has passed. The WWW class works in similar way. You can use an instance of that class to yield execution, returning only after the download has been completed, no need to manually poll the isDone property.
Keep in mind that you can only yield in Coroutines, I advise you read up on them. If you want to do a WWW request not in a Coroutine then you have to poll the isDone manually and proceed when completed.
To answer last question: Will the game freeze when creating an instance of the WWW class? - No. The class works asynchronously. You can use it in normal Update/Start etc. functions and inside Coroutines with yield.
if I make a WWW call and it takes several seconds to reply, doesn't the game freeze?
Not, Game Will not be freez try this code and you will see that log "after corou" will show immediately even the picture is downloading.
public class WWWDemo : MonoBehaviour {
bool b = true;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update()
{
if (b) {
StartCoroutine(ExampleCoroutine());
Debug.LogWarning("after corou");
}
}
IEnumerator ExampleCoroutine()
{
b = false;
Debug.Log("Time : " + DateTime.Now);
string url = "http://images.earthcam.com/ec_metros/ourcams/fridays.jpg";
WWW www = new WWW(url);
yield return www;
Debug.Log("Time : " + DateTime.Now);
Renderer render = gameObject.GetComponent<Renderer>();
render.material.mainTexture = www.texture;
Debug.Log("Time : " + DateTime.Now);
}
}
but the execution of next line will definitely stop after yield return www statement it will wait unitl the picture download while the rest events of Unity will execute async.
Consider below example as well separately. Start Event executes ones it will wait but update will run continuously.
IEnumerator Start() {
Debug.Log("Time : " + DateTime.Now);
string url = "http://images.earthcam.com/ec_metros/ourcams/fridays.jpg";
WWW www = new WWW(url);
yield return www;
Debug.Log("Time : " + DateTime.Now);
Renderer render = gameObject.GetComponent<Renderer>();
render.material.mainTexture = www.texture;
Debug.Log("Time : " + DateTime.Now);
}
void Update()
{
Debug.Log("update is running while start is waiting for downloading");
}

Using Background Worker and process.start not able to retrieve progress status

Good evening,
I just started playing around with C# and I tried creating a GUI for a program that runs in command line. I have been able to get it running, but now I am stuck trying to implement a progress bar to it.
I have read other post but I am unable to find the exact issue or to understand how to apply the solution to my issue.
Here is my code (apologize if this is very messy):
private void MethodToProcess(Object sender, DoWorkEventArgs args)
{
// Set all the strings for passthrough
String USMTPath_Work = USMTPath + USMTArch;
String USMTPath_full = USMTPath_Work + #"\Scanstate.exe";
String USMTFlags_Capture = #"/c /v:13 /o /l:scanstate.log /localonly /efs:copyraw";
String Argument_full = SavePath + XML1 + XML2 + USMTFlags_Capture;
// Test that USMT path is correct
if (USMTPath == null)
{
MessageBox.Show("Error: There is no USMT Path defined.");
return;
}
// Test that Windows folder is correct when offline
/* if (Windows_Path == null)
{
MessageBox.Show("Error: There is no Windows Path to capture.");
return;
} */
// Runs the capture
System.Diagnostics.Process Scanstate = new System.Diagnostics.Process();
Scanstate.StartInfo.FileName = USMTPath_full;
Scanstate.StartInfo.Arguments = Argument_full;
Scanstate.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
Scanstate.StartInfo.WorkingDirectory = USMTPath_Work;
//Scanstate.StartInfo.UseShellExecute = false;
Scanstate.StartInfo.CreateNoWindow = true;
//Scanstate.StartInfo.RedirectStandardOutput = true;
Scanstate.Start();
Scanstate.WaitForExit();
String Str_ExitCode = Scanstate.ExitCode.ToString();
if (Scanstate.ExitCode == 1)
MessageBox.Show("Error: Data has not been captured. Please check the log files for details.");
if (Scanstate.ExitCode == 0)
MessageBox.Show("Success: Data has been captured. For more information, check log files.");
else
{
MessageBox.Show("Error: Unknown error has occurred. Please check the log files for details.");
MessageBox.Show("Error Code: " + Str_ExitCode);
}
Scanstate.Close();
}
Basically, I am trying to run the process scanstate.exe. Now, I am trying to run backgroundworker in order to be able to retrieve progress and pass it to the progressbar.
private void btnCapture_Click(object sender, EventArgs e)
{
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
progressBar1.Step = 1;
BackgroundWorker CaptureBG = new BackgroundWorker();
CaptureBG.WorkerReportsProgress = true;
CaptureBG.DoWork += new DoWorkEventHandler(MethodToProcess);
CaptureBG.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(CaptureBG_RunWorkerCompleted);
CaptureBG.ProgressChanged += new ProgressChangedEventHandler(CaptureBG_ProgressChanged);
CaptureBG.RunWorkerAsync();
}
and
private void CaptureBG_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs args)
{
progressBar1.Value = 100;
}
private void CaptureBG_ProgressChanged(object sender, ProgressChangedEventArgs args)
{
progressBar1.Value++;
}
However I am either missunderstanding the use or I am missing something, since the process runs, but I don't get any progress on the progressbar. It only fills once the process finish.
What am I doing wrong? In general, how would a process report progress if I don't know exactly how long is going to take?
Thanks in advance
The BackgroundWorker is responsible for updating the progress as it gets further complete with its task.
There is no interaction between your process that you launch and your code that would provide progress of that process back to your code.
In order for this to work, two things have to happen:
You need to define a mechanism for your process to report progress to the BackgroundWorker.
The BackgroundWorker must update its own progress by calling the ReportProgress method so that the ProgressChanged event is fired.
The first step is the tricky one and depends on how scanstate.exe works. Does it do anything to give an indication of progress, such as write to the console? If so, you can redirect the console output and parse that output to determine or at least estimate progress.
UPDATE
Scanstate.exe provides the ability to write progress to a log, e.g.:
scanstate /i:migapp.xml /i:miguser.xml \\fileserver\migration\mystore /progress:prog.log /l:scanlog.log
You could use a FileWatcher in your BackgroundWorker to look for changes to the progress log and update progress accordingly.

Loading any persistent workflow containing delay activity when it is a runnable instance in the store

We are trying to load and resume workflows which have a delay. I have seen the Microsoft sample of Absolute Delay for this using store.WaitForEvents and LoadRunnableInstance to load the workflow. However here the workflow is already known.
In our case we want to have an event waiting for the store.WaitForEvents after every say 5 seconds to check if there is a runnable instance and if so only load and run that /those particular instances. Is there a way I could know which workflow instance is ready.
We are maintaing the workflow id and the xaml associated to it in our database, so if we could know the workflow instance id we could get the xaml mapped to it, create the workflow and then do a LOadRunnableInstance on it.
Any help would be greatly appreciated.
Microsoft sample (Absolute Delay)
public void Run(){
wfHostTypeName = XName.Get("Version" + Guid.NewGuid().ToString(),
typeof(WorkflowWithDelay).FullName);
this.instanceStore = SetupSqlpersistenceStore();
this.instanceHandle =
CreateInstanceStoreOwnerHandle(instanceStore, wfHostTypeName);
WorkflowApplication wfApp = CreateWorkflowApp();
wfApp.Run();
while (true)
{
this.waitHandler.WaitOne();
if (completed)
{
break;
}
WaitForRunnableInstance(this.instanceHandle);
wfApp = CreateWorkflowApp();
try
{
wfApp.LoadRunnableInstance();
waitHandler.Reset();
wfApp.Run();
}
catch (InstanceNotReadyException)
{
Console.WriteLine("Handled expected InstanceNotReadyException, retrying...");
}
}
Console.WriteLine("workflow completed.");
}
public void WaitForRunnableInstance(InstanceHandle handle)
{
var events=instanceStore.WaitForEvents(handle, TimeSpan.MaxValue);
bool foundRunnable = false;
foreach (var persistenceEvent in events)
{
if (persistenceEvent.Equals(HasRunnableWorkflowEvent.Value))
{
foundRunnable = true;
break;
}
}
if (!foundRunnable) {
Console.WriteLine("no runnable instance");
}
}
Thanks
Anamika
I had a similar problem with durable delay activities and WorkflowApplicationHost. Ended up creating my own 'Delay' activity that worked essentially the same way as the one out of the box, (takes an arg that describes when to resume the workflow, and then bookmarks itself). Instead of saving delay info in the SqlInstanceStore though, my Delay Activity created a record in a seperate db. (similar to the one you are using to track the Workflow Ids and Xaml). I then wrote a simple service that polled that DB for expired delays and initiated a resume of the necessary workflow.
Oh, and the Delay activity deleted it's record from that DB on bookmark resume.
HTH
I'd suggest having a separate SqlPersistenceStore for each workflow definition you're hosting.

Resources