Random time selection logic is not working and loops forever - powershell

My input is a text file with license plate and date for tire repair. I am then prompted to write a time e.g like 12:30. Then the script checks if the time is good or not. It's good if there is 30 minutes after and before not being used.
Using the example won't work since the 12:00-13:00 block contains an entry. Then the time isn't good, so the script has to search a good date for me. I tried to check line by line minute per minute. After, if the $help variable didn't change then it's good, but if it did, then make a random time, to check. Any ideas why that logic is not working?
Edit: Since I edited, the only thing I saw that isn't working, is that my script generates a random time, but it didn't check if that time is right, just accept that time and goodbye. Somehow I should make it to check it more times, and don't stop at the first generated time, check it, and if its still wrong, then randomize it again.
Sample data
ABC-145 8:30
BDE-540 9:45
EDS-598 10:30
SDF-478 11:30
HUT-434 12:15
JEC-238 13:15
ASD-325 14:00
VRA-123 16:15
HGV-456 18:00
$file = Get-Content Desktop/database.txt
[datetime]$time = Read-Host "Time for date?"
$start = $time
$check = $time
$help = 0
echo $time
foreach ($line in $file) {
#here I check if the date is free
$out = $line.Split()
while ($true) {
#until I found a good time
for ($i=0; $i -lt 30; $i++) {
#check the next half hour if good
if ($time -eq $out[1]) {
$help = 1
}
$time = $time.AddMinutes(1)
}
for ($i=0; $i -lt 60; $i++) {
#back check the half hour
if ($time -eq $out[1]) {
$help = 1
}
$time = $time.AddMinutes(-1)
}
$time = $time.AddMinutes(30)
if ($help -eq 0) {
break
} else {
$hour = Get-Random -Minimum 8 -Maximum 20
$minute = Get-Random -Minimum 0 -Maximum 59
$start = "$ora`:$perc"
$time = $start
$help = 0
#echo $time
}
}
}
if ($start-ne $check) {
echo "There wasnt a free date, but there is a free spot at " + $start
} else {
echo "This is free date"
}

I would recommend a different approach. First extract the times from your input file and convert them to DateTime values:
$times = Get-Content 'Desktop/database.txt' |
ForEach-Object { $_.Split("`t")[1] | Get-Date }
From that list determine the intervals that are actually wide enough to fit another appointment (at least 30 min after the previous and before the next appointment respectively, hence a minimum time window of 60 minutes):
$available = 0..($times.Count-1) |
Where-Object { ($times[$_+1]-$times[$_]).TotalMinutes -ge 60 }
Pick a random index from that list and add 30 minutes to the corresponding time:
$index = $available[(Get-Random -Maximum $available.Count)]
$start = $times[$index].AddMinutes(30)
'{0:HH:mm}' -f $start
Or, if you want some more variation, calculate a new starting time from the timeframe 30 minutes after the previous appointment to 30 minutes before the next appointment like this:
$index = $available[(Get-Random -Maxmimum $available.Count)]
$delta = ($times[$index+1]-$times[$index]).TotalMinutes - 60
$offset = 30 + [int](Get-Random -Maximum ($delta + 1))
$start = $times[$index].AddMinutes($offset)
'{0:HH:mm}' -f $start
This approach ensures that you don't run into an infinite loop when there are no available timeslots left.

Related

Powershell video length calculation

I have a calculation issue I cannot solve, any help appreciated! I receive video length of files in a more complex loop context using the following code:
$movs ="..\..\MOV"
$dura = New-Object -ComObject Shell.Application
$dura = Get-ChildItem -Path $movs -Recurse -Force | ForEach {
$Folder = $Shell.Namespace($_.DirectoryName)
$File = $Folder.ParseName($_.Name)
$Duration = $Folder.GetDetailsOf($File, 27)
[PSCustomObject]#{
vid-file= $_.Name -replace ".mov",""
duration = $Duration
}
}
Later on I match some IDs to $dura so that the result looks like this:
ID vid-file duration
1 move 00:01:08
1 run 00:01:12
1 fly 00:01:30
1 swim 00:01:08
1 sleep 00:02:20
2 move 00:01:08
2 swim 00:01:08
2 sleep 00:02:20
3 move 00:01:08
3 run 00:01:12
3 fly 00:01:30
3 swim 00:01:08
3 sleep 00:02:20
3 think 00:03:20
Now I need to calculate the starting points for each concatenated video case, i.e. I have to sum up the duration of the video for each part until the current position for every ID context and create a new column with it (every new ID starts at 00:00:00). The result would look like this:
ID vid-file duration videopart-start-at
1 move 00:01:08 00:00:00
1 run 00:01:12 00:01:08
1 fly 00:01:30 00:02:20
1 swim 00:01:08 00:03:50
1 sleep 00:02:20 00:04:58
2 move 00:01:08 00:00:00
2 swim 00:01:08 00:01:08
2 sleep 00:02:20 00:02:16
3 move 00:01:08 00:00:00
3 run 00:01:12 00:01:08
3 fly 00:01:30 00:02:20
3 swim 00:01:08 00:03:50
3 sleep 00:02:20 00:04:58
3 think 00:03:20 00:07:18
I think there could be some calculated object in the PSCustomObject but I can't figure it out..
[PSCustomObject]#{
vid-file= $_.Name -replace ".mov",""
duration = $Duration
videopart-start-at= $Duration | Measure-Object -Sum $Duration
}
Thanks, Daniel
I would think that there's an easier way of handling this - but I converted the time into seconds - then worked on the [TimeSpan] datatype.
$movs = 'c:\temp\sample' | Get-ChildItem -Recurse -Force -ErrorAction Stop
$dura = New-Object -ComObject Shell.Application
$result = Foreach ($mov in $movs) {
$Folder = $dura.Namespace($mov.DirectoryName)
$File = $Folder.ParseName($mov.Name)
$Duration = $Folder.GetDetailsOf($File, 27)
[PSCustomObject]#{
vidfile = $mov.Name -replace ".mov", ""
# Convert the string into an actual time data type
duration = $Duration
durationinseconds = ([TimeSpan]::Parse($Duration)).TotalSeconds
}
}
$i = 0
Foreach ($object in $result) {
# Skipping first and stopping on last (foreach will run out of objects to process)
if ($i -eq 0 -or $i -gt ($result.count)) {
# Adding one to counter
$i++
continue
}
$object.durationinseconds = $Object.durationinseconds + $result.durationinseconds[$i - 1]
$object.duration = [timespan]::fromseconds($object.durationinseconds)
("{0:hh\:mm\:ss}" -f $object.duration)
$i++
}
Thanks to Sebastian I found the following solution (I added the "startat" column in the pscustomobject, identical to durationinseconds):
$i = 0
Foreach ($object in $result) {
# skip lines gt result count
if ($i -gt ($result.count)) {
$i++
continue
}
# set start to 0 for first line
if ($i -eq 0) {
$object.startat = 0
}
# calculate start time for all following lines
if ($i -gt 0) {
$object.startat = $result.durationinseconds[$i - 1] + $result.startat[$i - 1]
}
# transform seconds to time value in duration var
$object.duration = [timespan]::fromseconds($object.startat)
# counter +1
$i++
}
$result
To calculate date/time differences, try something like this...
$current = Get-Date
$end = (Get-Date).AddHours(1)
$diff = New-TimeSpan -Start $current -End $end
"The time difference is: $diff"
# Results
<#
The time difference is: 01:00:00.0019997
#>
... then format as you need to.

Powershell Timed Function not returning correct time

I am writing a safety function for my powershell script so they do not run during working hours in case of disruptions. This means that I can automate more as I do not have to worry about the script taking too long as it will pause when needed then resume.
Unfortunately, when it is supposed to be running it is still sleeping. Any pointers on what I am missing would be appreciated.
Function WorkingHours {
$WorkingHours = "20:00-7:00"
if ($WorkingHours -match '^[0-9]{1,2}:[0-5][0-9]-[0-9]{1,2}:[0-5][0-9]$') {
$current = Get-Date
$start = Get-Date ($WorkingHours.split("-")[0])
$end = Get-Date ($WorkingHours.split("-")[1])
# correct for hours that span overnight
if (($end - $start).hours -lt 0) {
$start = $start.AddDays(-1)
}
# if the current time is past the start time
$startCheck = $current -ge $start
# if the current time is less than the end time
$endCheck = $current -le $end
# if the current time falls outside the window
if ((-not $startCheck) -or (-not $endCheck)) {
# sleep until the operational window starts again
$sleepSeconds = ($start - $current).TotalSeconds
Write-Host 'Starting to Sleep for'$sleepSeconds
if ($sleepSeconds -lt 0) {
# correct for hours that span overnight
$sleepSeconds = ($start.addDays(1) - $current).TotalSeconds
}
# sleep until the wake up interval
Start-Sleep -Seconds $sleepSeconds
}
}
}

Time-based while loop

I've been trying to get the while & the If loop to read in the time correctly, but it doesn't end the program when the right time has hit. I tried using single quotes '' and double quotes "", as well as different syntax like (-eq, -match, -ne) to see if any of those work....and they don't.
Program Goal: loops until it hits 07:00am
# While the value is 1.
while ($value -ne 2)
{
# Value should be 1 in order to stay in loop.
$value = 1
# Get's the time in 24hr format
$time = get-date -Format HH:mm:ss
# For Debugging; Writes out Time
Write-Output $time
# Creates a Pop-Up Windows that prevents the computer from timing out; runs every 15 minutes.
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("Operation Completed",0,"Done",0x1)
# Causes the Program to wait to send the Enter Keystroke.
Sleep 4
# Sends the Enter Keystroke.
$wshell.sendkeys('~')
# Causes the Program to wait to send the Enter Keystroke in seconds (900sec = 15 Minutes).
Sleep 4
# If Condition; If the time is over 2am then the program quits.
If ($time -eq "02:03:00")
{
# While Loop End Condition
$value = 2
# "Debugging Output"
Write-Output $value
Write-Output $time
}
Else
{
# While Loop Condition
$value = 1
# "Debugging Output"
Write-Output $value
Write-Output $time
}
}
# "Debugging Output"
Write-Output "End"
Write-Output $time
Write-Output $value
The chances are really low that your if-statement will become true. Because your while loop takes at least 8 (2x Start-Sleep and other work) seconds until a new beginning. That means the $time variable will probably never be exactly 02:03:00. In this case I would not go for the exact time. Instead I would check if it's 02:03:00 or later. Try that:
$time = Get-Date
if ($time -ge (Get-Date -Hour 02 -Minute 03 -Second 00))
{
}
This condition should do the work:
if ((Get-Date) -gt (Get-Date -Hour 7 -Minute 0 -Second 0)) {
# While Loop End Condition
$value = 2
# more actions
}
It's comparing the current time with the DateTime object with current day, but time set to 07:00:00.
Keep in mind two things:
It will allow the loop to run only between midnight and 7AM. If you want to start the script the day before you need to adjust the conditions.
It might be more readable to not use if, but put the condition directly in while() like this:
while ((Get-Date) -lt (Get-Date -Hour 7 -Minute 0 -Second 0)) {
# do something
}
Currently, you're checking for exact time, so in theory the end condition might be met, However, if it hits that specific line one second before/after, it won't stop the loop.
Finished Code (at least this version of it anyhow). Appreciate the help too :)!
<###
Program Goal: Prevent PC Timeout/Sleep; also loops until it hits 07:00am
Date: 10/14/19
Version: 1.2
###>
# Creates a Pop-Up Windows that prevents the computer from timing out; runs every 15 minutes.
$wshell = New-Object -ComObject Wscript.Shell
# Get's the time in 24hr format
$time = get-date
# While the value is not equal to 2.
while ($value -ne 2)
{
# Value should be 1 in order to stay in loop.
$value = 1
$wshell.Popup("Operation Completed", 5,"Done", 1)
# Causes the Program to wait to send the Enter Keystroke.
Sleep 2
# If Condition; If the time is over 7am then the program quits.
if ((Get-Date) -gt (Get-Date -Hour 7 -Minute 0 -Second 0))
{
# While Loop End Condition
$value = 2
}
else
{
# While Loop Condition
$value = 1
# Causes the Program to wait (900sec = 15 Minutes) to prevent PC Timeout, will re-run again after Sleep.
Sleep 900
}
}
# Cleans up the COM Object
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wshell) | Out-Null

How to compare time between two variables

I am trying to compare the time difference between two variables. $dt is a string pulled from the text of a log file. $curtime is pulled from the current date and time.
If I output $dt, it displays correctly the date and time from the log files. If I output $curtime, it displays correctly the date and time from the workstation.
But when I try and run New-TimeSpan and output the difference, my response is always "02:24:12".
Here is the code I am using:
New-TimeSpan $dt $curtime | Set-Variable -Name $TimeDiff
if ($TimeDiff.Seconds -lt 0) }
$Hrs = ($TimeDiff.Hours) + 23
$Mins = ($TimeDiff.Minutes) + 59
$Secs = ($TimeDiff.Seconds) + 59 }
else {
$Hrs = $TimeDiff.Hours
$Mins = $TimeDiff.Minutes
$Secs = $TimeDiff.Seconds }
$Difference = '{0:00}:{1:00}:{2:00}' -f $Hrs,$Mins,$Secs
$Difference
Unless he datetime from the logs are from a very narrow timespan,
it makes IMO no sense to manipulate by adding 23:59:59.
Better take the integer part from .TotalHours
Here returning hours > 24/99
$dt = "05/26/19 08:23:37"
$LogDT = [datetime]::parseexact($dt,"MM/dd/yy HH:mm:ss",[cultureInfo]::Invariantculture)
$TimeDiff = [datetime]::now - $LogDT
$Difference = '{0:00}:{1:00}:{2:00}' -f ([math]::floor($Timediff.TotalHours)),
$Timediff.Minutes,$Timediff.Seconds
Sample output:
> $Difference
111:16:17

Efficient way to find and replace many strings in a large text file

The Text file contains a software output on a time domain analysis. 10800 seconds simulation and 50 nodes being considered. We have 540,000 strings to be replaced in 540 MB text file with 4.5 million lines.
Which is currently projected to take more than 4 days. Something is going wrong. Don't know what. Please suggest me a better efficient approach.
Below is the function which does the find and replace.
To replace the string the script goes through the original text file line by line at the same time it generates a duplicate file with replaced strings. So another 540 MB file with 4.5 million lines will be generated at the end of the script.
Function ReplaceStringsInTextFile
{
$OutputfilebyLine = New-Object -typename System.IO.StreamReader $inputFilePathFull
$uPreviousValue = 0
$time = 60
$u = 0; $LastStringWithoutFindResult = 0
$lineNumber = 0
while ($null -ne ($line = $OutputfilebyLine.ReadLine())) {
$lineNumber = $lineNumber + 1
if ($time -le $SimulationTimeSeconds) # time simulation start and end checks
{
# 10800 strings corresponds to one node
# there are 50 nodes.. Thus 540,000 values
# $StringsToFindFileContent contains strings to find 540,000 strings
# $StringsToReplaceFileContent contains strings to replace 540,000 strings
$StringToFindLineSplit = -split $StringsToFindFileContent[$time-60]
$StringToReplaceLineSplit = -split $StringsToReplaceFileContent[$time-60]
if($u -le $NumberofNodes-1)
{
$theNode = $Nodes_Ar[$u]
$StringToFindvalue = $StringToFindLineSplit[$u]
$StringToReplacevalue = $StringToReplaceLineSplit[$u]
if (($line -match $theNode) -And ($line -match $StringToFindvalue)){
$replacedLine = $line.replace($StringToFindvalue,$StringToReplacevalue)
add-content -path $WriteOutputfilePathFull -value "$replacedLine"
$uPreviousValue = $u
$checkLineMatched = 1
if (($line -match $LastNodeInArray)) {
$time = $time + 1
$LastStringWithoutFindResult = 0
}
} elseIf (($line -match $LastNodeInArray) -And ($checkLineMatched -eq 0)) {
$LastStringWithoutFindResult = $LastStringWithoutFindResult + 1
} else {
#"Printing lines without match"
add-content -path $WriteOutputfilePathFull -value "$line"
$checkLineMatched = 0
}
}
if ($checkLineMatched -eq 1) {
# incrementing the value of node index to next one in case the last node is found
$u = $uPreviousValue + 1
if ($u -eq $Nodes_Ar.count) {
$u = 0
$timeElapsed = (get-date -displayhint time) - $startTime
"$($timeElapsed.Hours) Hours $($timeElapsed.Minutes) Minutes $($timeElapsed.Seconds) Seconds"
}
}
}
# Checking if the search has failed for more than three cycles
if ($LastStringWithoutFindResult -ge 5) { # showing error dialog in case of search error
[System.Windows.Forms.MessageBox]::Show("StringToFind Search Fail. Please correct StringToFind values. Aborting now" , "Status" , 0)
$OutputfilebyLine.close()
}
}
$OutputfilebyLine.close()
}
The above function is the last part of the script. Which is taking the most time.
I had run the script in under 10 hours 1 year ago.
Update The script sped up running after 4 hours and suddenly time to complete projection reduced from 4 days to under 3 hours. The script finished running in 7 hours and 9 minutes. However i am not sure what made the sudden change in speed other than asking the question on stack overflow :)
As per the suggestion by https://stackoverflow.com/users/478656/tessellatingheckler
I have avoided writing one line at a time using
add-content -path $WriteOutputfilePathFull -value "$replacedLine"
Instead i am now writing ten thousand lines at a time using add-content
$tenThousandLines = $tenThousandLines + "`n" + $replacedLine
And at the appropriate time I am using add-content to write 10,000 lines at one go like below. The if block follows my methods logic
if ($lineNumber/10000 -gt $tenThousandCounter){
clear-host
add-content -path $WriteOffpipeOutputfilePathFull -value "$tenThousandLines"
$tenThousandLines = ""
$tenThousandCounter = $tenThousandCounter + 1
}
I have encountered system out of memmory exception error when trying to add 15,000 or 25,000 lines at a time. After using this the time required for the operation has reduced from 7 hours to 5 hours. And at another time to 2 hours and 36 minutes.