I'm trying to write a program which counts the number of mouse left and right buttons presses every 30 seconds since start in order to get statistics of mouse activity. But it doesn't count single rightclick, it counts rightclick as leftclick + rightclick combination. Also it doesn't output results after 30 seconds even if there was no clicks at all, instead it waits mouse button pressing after 30 seconds passed. I'm stumped. What should I change to fix this? Here is my code.
package main
import (
"fmt"
"github.com/go-vgo/robotgo"
"time"
)
const t = 30
func leftclickcounter () (int) {
nowTime := time.Now().Unix()
nowTime = time.Now().Unix()
NumOfLeftClick := 0
for {
lmb := robotgo.AddMouse("left" )
if lmb {
fmt.Println("leftclick")
NumOfLeftClick++
}
if time.Now().Unix()-nowTime >= t {
return NumOfLeftClick
}
}
}
func rightclickcounter () (int) {
nowTime := time.Now().Unix()
nowTime = time.Now().Unix()
NumOfRightClick := 0
for {
rmb := robotgo.AddMouse("right")
if rmb {
fmt.Println("rightclick")
NumOfRightClick++
}
if time.Now().Unix() - nowTime >= t {
return NumOfRightClick
}
}
}
func main() {
for {
lc := leftclickcounter()
rc := rightclickcounter()
fmt.Println("Number of leftclicks:", lc)
fmt.Println("Number of rightclicks:", rc)
}
var input string
fmt.Scanln(&input)
}
Besides the elapsed time mentioned by Flimzy
You are running an infinite loop within an infinite loop, rightclickcounter() gets never executed. At least not in your timeframe.
Currently, you are starting lc counter -> wait 30 secs -> start rc counter -> wait 30 secs
for {
lc := leftclickcounter()
rc := rightclickcounter()
fmt.Println("Number of leftclicks:", lc)
fmt.Println("Number of rightclicks:", rc)
}
take a look at https://gobyexample.com/goroutines and https://tour.golang.org/concurrency/2
Related
I have the following piece of code. I'm trying to run 3 GO routines at the same time never exceeding three. This works as expected, but the code is supposed to be running updates a table in the DB.
So the first routine processes the first 50, then the second 50, and then third 50, and it repeats. I don't want two routines processing the same rows at the same time and due to how long the update takes, this happens almost every time.
To solve this, I started flagging the rows with a new column processing which is a bool. I set it to true for all rows to be updated when the routine starts and sleep the script for 6 seconds to allow the flag to be updated.
This works for a random amount of time, but every now and then, I'll see 2-3 jobs processing the same rows again. I feel like the method I'm using to prevent duplicate updates is a bit janky and was wondering if there was a better way.
stopper := make(chan struct{}, 3)
var counter int
for {
counter++
stopper <- struct{}{}
go func(db *sqlx.DB, c int) {
fmt.Println("start")
updateTables(db)
fmt.Println("stop"b)
<-stopper
}(db, counter)
time.Sleep(6 * time.Second)
}
in updateTables
var ids[]string
err := sqlx.Select(db, &data, `select * from table_data where processing = false `)
if err != nil {
panic(err)
}
for _, row:= range data{
list = append(ids, row.Id)
}
if len(rows) == 0 {
return
}
for _, row:= range data{
_, err = db.Exec(`update table_data set processing = true where id = $1, row.Id)
if err != nil {
panic(err)
}
}
// Additional row processing
I think there's a misunderstanding on approach to go routines in this case.
Go routines to do these kind of work should be approached like worker Threads, using channels as the communication method in between the main routine (which will be doing the synchronization) and the worker go routines (which will be doing the actual job).
package main
import (
"log"
"sync"
"time"
)
type record struct {
id int
}
func main() {
const WORKER_COUNT = 10
recordschan := make(chan record)
var wg sync.WaitGroup
for k := 0; k < WORKER_COUNT; k++ {
wg.Add(1)
// Create the worker which will be doing the updates
go func(workerID int) {
defer wg.Done() // Marking the worker as done
for record := range recordschan {
updateRecord(record)
log.Printf("req %d processed by worker %d", record.id, workerID)
}
}(k)
}
// Feeding the records channel
for _, record := range fetchRecords() {
recordschan <- record
}
// Closing our channel as we're not using it anymore
close(recordschan)
// Waiting for all the go routines to finish
wg.Wait()
log.Println("we're done!")
}
func fetchRecords() []record {
result := []record{}
for k := 0; k < 100; k++ {
result = append(result, record{k})
}
return result
}
func updateRecord(req record) {
time.Sleep(200 * time.Millisecond)
}
You can even buffer things in the main go routine if you need to update all the 50 tables at once.
This is rather simple, but I can't figure it out...
I am trying to make a time.Tick every seconds... but it ticks every 2 seconds?!?
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Hello, playground")
iterationIndex := 0
tick := time.Tick(time.Second)
done := make(chan bool, 1)
for _ = range tick {
select {
case <-tick:
iterationIndex++
if iterationIndex >= 10{
done <- true
}
fmt.Printf("%s\n", time.Now().Format("15:04:05.000000"))
case <-done:
return
}
}
}
links to go playground
https://play.golang.org/p/WKhnNK2BRpd
This is where the mistake is:
for _ = range tick {
select {
case <-tick:
What happens is that you get one tick through for _ = range tick, and another tick through select { case <-tick:. And you log the second tick always.
To log every tick you need to do this:
for {
select {
case <-tick:
iterationIndex++
if iterationIndex >= 10{
done <- true
}
fmt.Printf("%s\n", time.Now().Format("15:04:05.000000"))
case <-done:
return
}
}
Here you handle and log each tick.
EDIT:
If you really want to use the for ... range statement instead, you can do something like this:
for _ = range tick{
if iterationIndex >= 10{
break
}
iterationIndex++
fmt.Printf("%s\n", time.Now().Format("15:04:05.000000"))
}
I am trying to find a way to check if the current time is inside a time window.
The inputs are:
upgradeDay []string - a slice of days (for instance ["Sunday", "Tuesday"])
upgradetime string - hour:minute (for instance "22:04")
upgradeDuration int64 - amount of time, from the upgradetime at which the time window is valid. it can be up to 12 hours.
Full example:
upgradeDay = ["Sunday", Tuesday"] , upgradetime = "10:00", upgradeDuration = 2 -> the time windows is at every Sunday and Tuesday, from 10:00 to 12:00 o'clock.
I tried to write the following function, but it's not working in transition between days/months/years:
func isInsideTimeWindow(upgradeDay []string, upgradeTime string, upgradeDuration int64) bool {
now := time.Now()
ut := strings.Split(upgradeTime, ":")
hour, _ := strconv.Atoi(ut[0])
min, _ := strconv.Atoi(ut[1])
// !! not working when now it's Monday 00:01 and got: upgradeDay = ["Sunday"], upgradeTime = 23:59, upgradeDuration = 2
twStart := time.Date(now.Year(), now.Month(), now.Day(), hour, min, 0, 0, now.Location())
twEnd := twStart.Add(time.Hour * time.Duration(upgradeDuration))
if !(now.After(twStart) && now.Before(twEnd)) {
return false
}
wd := now.Weekday().String()
for i := range upgradeDay {
if upgradeDay[i] == wd {
return true
}
}
return false
}
Does someone got an idea on how to solve that in Go?
Here is one approach to the problem:
package main
import "time"
type window struct { time.Time }
func (w window) isDay(s string) bool {
return w.Weekday().String() == s
}
func (w window) isHourRange(begin, end int) bool {
return w.Hour() >= begin && w.Hour() <= end
}
func main() {
w := window{
time.Now(),
}
{
b := w.isDay("Friday")
println(b)
}
{
b := w.isHourRange(20, 23)
println(b)
}
}
This assume only one day is valid, so you would need to modify this to handle
multiple days. This should get you started though.
There is a lot of complexity in times. For instance:
What if an upgrade day is "Søndag" (Danish) instead of "Sunday"?
Should we work in local time, or UTC? If local, whose location counts? If the server is in London and I am in San Francisco, do we use the server's time, or my time?
If the upgrade interval includes 2 AM, does that count 2 AM PDT and then 2 AM PST as well? These times are one hour apart where I live. If the interval starts at 2 AM and ends at 2:59:59, that time does not exist on one day of the year in many areas with one hour DST shift.
If you get to ignore all these complexities—internationalization (i18n), localization (l10n), DST, and so on—there's still a bit of a problem with the fact that someone can set the date and time, or the upgrade itself might take some time, but usually we get to ignore these too.
Note that Go's time.Now() returns local time—but, whose location? As we have not yet answered the whose time zone to use question yet, we might want to avoid worrying about this. Given the rest of your input constraints, let's write a function to determine if a supplied time meets the input constraints, rather than if time.Now() does so. The caller can then provide either a UTC time or a wall-clock time in the user's location:
someNow = time.Time()
localNow = someNow.In(location) // from time.LoadLocation() or similar
We also have something that seems at odds with your types:
upgradeDuration int64 - amount of time, from the upgradetime at which the time window is valid. it can be up to 12 hours
A value in hours that is between 0 and 12 inclusive fits easily in plain int. Is this already a time.Duration value expressed in nanoseconds? If so, why is it int64 and not time.Duration? Or is it a value in seconds, and therefore can be between 0 and 43200? If so, it still fits in int.
I made a bunch of assumptions and came up with the following, which you can try out on the Go Playground.
package main
import (
"fmt"
"strconv"
"strings"
"time"
)
// startOK determines whether the given starting-time is within a
// time window.
//
// The window starts at a time given as two integers,
// h and m, representing hours and minutes, and extends for
// the given duration d in hours, which in general should not
// extend into another day. If it does extend past the end of
// the day into the next day, we ignore the extension.
//
// The days on which the time *is* in that window are further
// limited by the days[] slice of Weekday values.
//
// Note: it would probably be sensible to return a time.Duration
// value that is how long it will be until the next OK time, but
// we leave that as an exercise.
//
// It would also be sensible to allow the duration d to extend
// into the next day, which is also left as an exercise.
func startOK(when time.Time, days []time.Weekday, h, m, d int) bool {
// Find OK-to-start time, and end-time. If end exceeds
// 24*60, we ignore the extra end time, rather than
// allowing some minutes into the next day.
start := h*60 + m
end := start + d*60
// Convert when to hour-and-minute and see if we are
// in the allowed range.
wh, wm, _ := when.Clock()
now := wh*60 + wm
if now < start || now >= end {
// Not in hh:mm through hh+d:mm; say no.
return false
}
// The time-of-day is OK; check the day-of-week.
// We could do this earlier but by positioning it
// here, we leave room to check to see if it's
// the *next* day, if needed.
if !func(wd time.Weekday) bool {
for _, allowed := range days {
if wd == allowed {
return true
}
}
return false
}(when.Weekday()) {
return false // when.Weekday() not in days[]
}
// time is OK, day is OK
return true
}
// startOKstr is like startOK but the window starts at a time
// given as a string encoded as hh:mm, with the days being a
// slice of strings instead of Weekday. Because of these strings,
// parsing can produce an error, so this function has an error
// return.
func startOKStr(when time.Time, days []string, hhmm string, d int) (bool, error) {
parts := strings.Split(hhmm, ":")
// optional: be strict about two-digit values
if len(parts) != 2 {
return false, fmt.Errorf("invalid time string %q", hhmm)
}
h, err := strconv.Atoi(parts[0])
if err != nil {
return false, err
}
if h < 0 || h >= 60 {
return false, fmt.Errorf("invalid hour value %s", parts[0])
}
m, err := strconv.Atoi(parts[1])
if err != nil {
return false, err
}
if m < 0 || m >= 60 {
return false, fmt.Errorf("invalid minute value %s", parts[1])
}
var wd []time.Weekday
for _, s := range days {
w, err := parseWeekday(s)
if err != nil {
return false, err
}
wd = append(wd, w)
}
ok := startOK(when, wd, h, m, d)
return ok, nil
}
// parseWeekday handles weekday strings.
//
// Ideally we'd use time.Parse for this, as it already has
// these in it, but they are not exported in usable form.
func parseWeekday(s string) (time.Weekday, error) {
strToWeekday := map[string]time.Weekday{
"Sunday": time.Sunday,
"Monday": time.Monday,
"Tuesday": time.Tuesday,
"Wednesday": time.Wednesday,
"Thursday": time.Thursday,
"Friday": time.Friday,
"Saturday": time.Saturday,
}
if v, ok := strToWeekday[s]; ok {
return v, nil
}
return time.Sunday, fmt.Errorf("invalid day-of-week %q", s)
}
// tests should be converted to real tests and put in
// a separate file.
func tests() {
okDays := []string{"Sunday", "Wednesday"}
okStart := "04:00"
okDuration := 2 // hours
tfmt := "Mon Jan 2 15:04:05 2006"
t1 := "Sat Sep 5 04:30:00 2020" // time OK, day not
t2 := "Sun Sep 6 04:30:00 2020" // time OK, day OK
check := func(s string, expect bool) {
when, err := time.Parse(tfmt, s)
if err != nil {
panic(err)
}
result, err := startOKStr(when, okDays, okStart, okDuration)
if err != nil {
panic(err)
}
if result != expect {
fmt.Printf("fail: expected %v for %q\n", expect, s)
}
}
check(t1, false)
check(t2, true)
fmt.Println("2 tests run")
}
func main() {
tests()
}
I would like to build a program which runs a number of cron jobs which start at a time which, in general, is in the past. Here is a simplified example using gocron:
package main
import (
"time"
"github.com/jasonlvhit/gocron"
"github.com/sirupsen/logrus"
)
// This slice would be obtained from persistent storage
var startTimes = []time.Time{
time.Now().Add(-4 * time.Second),
time.Now().Add(-3 * time.Second),
}
func format(t time.Time) string {
return t.Format("15:04:05")
}
func notify(startTime time.Time) {
logrus.WithField("time", format(time.Now())).Infof("I started at %s\n", format(startTime))
}
func main() {
for _, startTime := range startTimes {
gocron.Every(10).Seconds().From(&startTime).Do(notify, startTime)
}
logrus.Infof("Starting at %s...\n", format(time.Now()))
<-gocron.Start()
}
If I run this, I get the following output:
INFO[0000] Starting at 00:30:54...
INFO[0010] I started at 00:30:50 fields.time="00:31:04"
INFO[0010] I started at 00:30:51 fields.time="00:31:04"
What I observe is that all the events are happening at once, 10 seconds after I start the program.
However, since the startTimes are 4 and 3 seconds before the program starts, what I would like is for the events to occur 6 and 7 seconds after the program starts (and every 10 seconds after that), respectively.
Is there a way to do this with gocron or some other tool?
This seems like just simple math:
interval := 10 * time.Second
nextTime := time.Now().Add((time.Since(startTime) + interval) % interval)
gocron.Every(10).Seconds().From(&nextTime).Do(notify, nextTime)
https://play.golang.org/p/pwEZqy_LUuk
We can try to leverage a simple ticker, it is not a complete solution but should be easy enough to adapt.
package main
import (
"fmt"
"time"
)
func ticker(period time.Duration, length ...time.Duration) <-chan time.Time {
ticker := time.NewTicker(period)
if len(length) > 0 {
done := make(chan bool)
go func() {
time.Sleep(period + length[0])
done <- true
}()
go func() {
<-done
ticker.Stop()
}()
}
return ticker.C
}
func main() {
t1 := ticker(6*time.Second, 4*time.Second)
t2 := ticker(7*time.Second, 3*time.Second)
t3 := ticker(10 * time.Second)
for {
select {
case t1 := <-t1:
fmt.Println("t1: ", t1)
case t2 := <-t2:
fmt.Println("t2: ", t2)
case t3 := <-t3:
fmt.Println("t3: ", t3)
}
}
}
I have been looking around for examples on how to implement a function that allows you to execute tasks at a certain time in Go, but I couldn't find anything.
I implemented one myself and I am sharing it in the answers, so other people can have a reference for their own implementation.
This is a general implementation, which lets you set:
interval period
hour to tick
minute to tick
second to tick
UPDATED: (the memory leak was fixed)
import (
"fmt"
"time"
)
const INTERVAL_PERIOD time.Duration = 24 * time.Hour
const HOUR_TO_TICK int = 23
const MINUTE_TO_TICK int = 00
const SECOND_TO_TICK int = 03
type jobTicker struct {
timer *time.Timer
}
func runningRoutine() {
jobTicker := &jobTicker{}
jobTicker.updateTimer()
for {
<-jobTicker.timer.C
fmt.Println(time.Now(), "- just ticked")
jobTicker.updateTimer()
}
}
func (t *jobTicker) updateTimer() {
nextTick := time.Date(time.Now().Year(), time.Now().Month(),
time.Now().Day(), HOUR_TO_TICK, MINUTE_TO_TICK, SECOND_TO_TICK, 0, time.Local)
if !nextTick.After(time.Now()) {
nextTick = nextTick.Add(INTERVAL_PERIOD)
}
fmt.Println(nextTick, "- next tick")
diff := nextTick.Sub(time.Now())
if t.timer == nil {
t.timer = time.NewTimer(diff)
} else {
t.timer.Reset(diff)
}
}
In case someone drops in on this question searching for a quick solution.
I found a neat library that makes it really easy to schedule jobs.
Link: https://github.com/jasonlvhit/gocron
The API is pretty straightforward:
import (
"fmt"
"github.com/jasonlvhit/gocron"
)
func task() {
fmt.Println("Task is being performed.")
}
func main() {
s := gocron.NewScheduler()
s.Every(2).Hours().Do(task)
<- s.Start()
}
the answer provided by #Daniele B is not good enough, as #Caleb says, that implementation leaks memory, because each time we create a new ticker, the old one will never be released.
so I wrap the time.timer, and reset it everytime, a example here:
package main
import (
"fmt"
"time"
)
const INTERVAL_PERIOD time.Duration = 24 * time.Hour
const HOUR_TO_TICK int = 23
const MINUTE_TO_TICK int = 21
const SECOND_TO_TICK int = 03
type jobTicker struct {
t *time.Timer
}
func getNextTickDuration() time.Duration {
now := time.Now()
nextTick := time.Date(now.Year(), now.Month(), now.Day(), HOUR_TO_TICK, MINUTE_TO_TICK, SECOND_TO_TICK, 0, time.Local)
if nextTick.Before(now) {
nextTick = nextTick.Add(INTERVAL_PERIOD)
}
return nextTick.Sub(time.Now())
}
func NewJobTicker() jobTicker {
fmt.Println("new tick here")
return jobTicker{time.NewTimer(getNextTickDuration())}
}
func (jt jobTicker) updateJobTicker() {
fmt.Println("next tick here")
jt.t.Reset(getNextTickDuration())
}
func main() {
jt := NewJobTicker()
for {
<-jt.t.C
fmt.Println(time.Now(), "- just ticked")
jt.updateJobTicker()
}
}
I have created a package that actually supports crontab syntax if you are familiar with it, for example:
ctab := crontab.New()
ctab.AddJob("*/5 * * * *", myFunc)
ctab.AddJob("0 0 * * *", myFunc2)
Package link: https://github.com/mileusna/crontab
This is another general implementation without need for a third party library.
Disclaimer: This implementation works with UTC. For managing timezones it has to be modified.
Run a func once a day at noon.
Period: time.Hour * 24
Offset: time.Hour * 12
Run a func twice a day at 03:40 (00:00 + 03:40) and 15:40 (12:00 + 03:40).
Period: time.Hour * 12
Offset: time.Hour * 3 + time.Minute * 40
Updated (2020-01-28):
Changes:
context.Context can be used for cancellation, makes it testable.
time.Ticker removes the need for calculating the time of the next execution.
package main
import (
"context"
"time"
)
// Schedule calls function `f` with a period `p` offsetted by `o`.
func Schedule(ctx context.Context, p time.Duration, o time.Duration, f func(time.Time)) {
// Position the first execution
first := time.Now().Truncate(p).Add(o)
if first.Before(time.Now()) {
first = first.Add(p)
}
firstC := time.After(first.Sub(time.Now()))
// Receiving from a nil channel blocks forever
t := &time.Ticker{C: nil}
for {
select {
case v := <-firstC:
// The ticker has to be started before f as it can take some time to finish
t = time.NewTicker(p)
f(v)
case v := <-t.C:
f(v)
case <-ctx.Done():
t.Stop()
return
}
}
}
Original:
package main
import (
"time"
)
// Repeat calls function `f` with a period `d` offsetted by `o`.
func Repeat(d time.Duration, o time.Duration, f func(time.Time)) {
next := time.Now().Truncate(d).Add(o)
if next.Before(time.Now()) {
next = next.Add(d)
}
t := time.NewTimer(next.Sub(time.Now()))
for {
v := <-t.C
next = next.Add(d)
t.Reset(next.Sub(time.Now()))
f(v)
}
}
I'm using https://github.com/ehsaniara/gointerlock. It's also supported in distributed systems and has a builtin distributer lock (Redis)
import (
"context"
"fmt"
"github.com/ehsaniara/gointerlock"
"log"
"time"
)
var job = gointerlock.GoInterval{
Interval: 2 * time.Second,
Arg: myJob,
}
err := job.Run(ctx)
if err != nil {
log.Fatalf("Error: %s", err)
}
func myJob() {
fmt.Println(time.Now(), " - called")
}