How to use Quartz.net with F# - f#

I cannot make F# to schedule simple scheduler based on this .
open System
open Quartz
open Quartz.Impl
let schedulerFactory = StdSchedulerFactory()
let scheduler = schedulerFactory.GetScheduler()
scheduler.Start()
type Job () =
interface IJob with
member x.Execute(context: IJobExecutionContext) =
task{ //
Console.WriteLine(DateTime.Now)
}
let job = JobBuilder.Create<Job>().Build() //Exception Unhandled error!
let trigger =
TriggerBuilder.Create()
.WithSimpleSchedule(fun x ->
x.WithIntervalInSeconds(1).RepeatForever() |> ignore)
.Build()
scheduler.Result.ScheduleJob(job, trigger) |> ignore
Threading.Thread.Sleep(10000000)
Console.ReadKey()|>ignore
It gives runtime error for line let job = JobBuilder.Create<Job>().Build() System.InvalidOperationException: 'Start may not be called on a task that has completed.'
I also have had to change the scheduler.ScheduleJob to the scheduler.Result.ScheduleJob(.. from the original example above.

Based on the documentation that Bent pointed to, I think this should do it, using the task builder from F# 6:
open System
open System.Threading.Tasks
open Quartz
open Quartz.Impl
type Job() =
interface IJob with
member _.Execute(_context) =
Console.Out.WriteLineAsync(DateTime.Now.ToString())
task {
// Grab the Scheduler instance from the Factory
let factory = StdSchedulerFactory()
let! scheduler = factory.GetScheduler()
// and start it off
do! scheduler.Start()
// define the job and tie it to our Job class
let job =
JobBuilder.Create<Job>()
.WithIdentity("job1", "group1")
.Build()
// Trigger the job to run now, and then repeat every second
let trigger =
TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartNow()
.WithSimpleSchedule(fun x ->
x.WithIntervalInSeconds(1).RepeatForever()
|> ignore)
.Build()
// Tell quartz to schedule the job using our trigger
let! _offset = scheduler.ScheduleJob(job, trigger)
// some sleep to show what's happening
do! Task.Delay(TimeSpan.FromSeconds(10))
// and last shut down the scheduler when you are ready to close your program
do! scheduler.Shutdown()
} |> ignore
Console.ReadLine() |> ignore
Output is something like:
4/16/2022 6:11:52 PM
4/16/2022 6:11:52 PM
4/16/2022 6:11:53 PM
4/16/2022 6:11:54 PM
4/16/2022 6:11:55 PM
4/16/2022 6:11:56 PM
4/16/2022 6:11:57 PM
4/16/2022 6:11:58 PM
4/16/2022 6:11:59 PM
4/16/2022 6:12:00 PM
4/16/2022 6:12:01 PM

Related

F# propper remove of ElapsedEventHandler handler

Hi I have a question I have quick program that will do something in every 10 seconds and it looks simple:
[<EntryPoint>]
let main argv =
let timer = new Timer(float 10000)
let OnTimedEvent (frameId:uint32) : ElapsedEventHandler = new ElapsedEventHandler (fun obj args -> printfn "DATE: %A FRAME: %i" DateTime.Now frameId)
while(true) do
let keyStroke = Console.ReadKey()
if keyStroke.Key.Equals(ConsoleKey.Enter) then
let frameId = 1u
timer.AutoReset <- true
timer.Elapsed.RemoveHandler(OnTimedEvent frameId)
timer.Elapsed.AddHandler(OnTimedEvent frameId)
timer.Start();
else
printfn "%c pressed" keyStroke.KeyChar
0
Problem is I can not properly remove handler as it is, If i press enter once it starts and gives me one message every 10 sec so thi is what I am aiming for. But if i press enter 3 times it increments and gives me 3 messeges and so on, but i only want one.
Another thing is that if I remove parameter from it it works perfectly, so i suppose problem is with parameter. Any solutions for this?
The problem with your current implementation is that every call to OnTimedEvent returns a new instance of the ElapsedEventHandler. When you call it as follows:
timer.Elapsed.RemoveHandler(OnTimedEvent frameId)
you are removing a new handler that has not previously been registerd and so nothing actually happens. When you change your code to add/remove the same handler, then you are always using the same instance:
let frameId = 1u
let timedHandler = new ElapsedEventHandler (fun obj args ->
printfn "DATE: %A FRAME: %i" DateTime.Now frameId)
timer.Elapsed.RemoveHandler(timedHandler)
timer.Elapsed.AddHandler(timedHandler)
Now you do not have a good way of passing the frameId to your event handler. In your code, frameId is always 1u and so it's hard to see what you actually want, but you could make it mutable:
let mutable frameId = 1u
let timedHandler = new ElapsedEventHandler (fun obj args ->
printfn "DATE: %A FRAME: %i" DateTime.Now frameId)
frameId <- 2u
timer.Elapsed.RemoveHandler(timedHandler)
timer.Elapsed.AddHandler(timedHandler)
That said, it's not really clear what you are trying to do and perhaps there is an altogether different way of doing what you want.
A completely different approach would be to use MailboxProcessor that keeps the current frameId and handles two types of messages - one triggered every 10 seconds by a timer and one that can be used to change the frame ID:
type Message =
| Tick
| ChangeFrameId of uint32
let agent = MailboxProcessor.Start(fun inbox ->
let rec run frameId = async {
let! msg = inbox.Receive()
match msg with
| ChangeFrameId newId ->
return! run newId
| Tick ->
printfn "DATE: %A FRAME: %i" DateTime.Now frameId
return! run frameId }
run 1u)
let timer = new Timer(float 10000, AutoReset = true)
timer.Elapsed.Add(fun _ -> agent.Post(Tick))
timer.Start()
agent.Post(ChangeFrameId 2u)
This code refactors what you have to store the handler so that it can be removed.
open System
open System.Timers
[<EntryPoint>]
let main argv =
let timer = new Timer(float 10000, AutoReset = true)
let onTimedEvent (frameId: uint32) : ElapsedEventHandler = new ElapsedEventHandler (fun obj args -> printfn "DATE: %A FRAME: %i" DateTime.Now frameId)
let rec readKey frameId =
let handler = onTimedEvent frameId
timer.Elapsed.AddHandler(handler)
timer.Start()
let keyStroke = Console.ReadKey()
timer.Stop()
timer.Elapsed.RemoveHandler(handler)
printfn "%c pressed" keyStroke.KeyChar
let nextFrameId =
if keyStroke.Key.Equals(ConsoleKey.Enter) then
frameId + 1u
else
frameId
readKey(nextFrameId)
readKey(1u)
0
There may be better ways to accomplish what you are after, but this answers your question.

F# program runs correctly in fsi, but hangs as an exe

I have a piece of code that adds a row to a database when a MailboxProcessor receives a message. It works correctly when run in fsi, but it hangs when compiled to an exe. The script is as follows:
#r "../packages/Newtonsoft.Json/lib/net40/Newtonsoft.Json.dll"
#r "../packages/SQLProvider/lib/FSharp.Data.SqlProvider.dll"
open Newtonsoft.Json
open FSharp.Data.Sql
open System
let [<Literal>] ResolutionPath = __SOURCE_DIRECTORY__ + "/../build/"
let [<Literal>] ConnectionString = "Data Source=" + __SOURCE_DIRECTORY__ + #"/test.db;Version=3"
// test.db is initialized as follows:
//
// BEGIN TRANSACTION;
// CREATE TABLE "Events" (
// `id`INTEGER PRIMARY KEY AUTOINCREMENT,
// `timestamp` DATETIME NOT NULL
// );
// COMMIT;
type Sql = SqlDataProvider<
ConnectionString = ConnectionString,
DatabaseVendor = Common.DatabaseProviderTypes.SQLITE,
ResolutionPath = ResolutionPath,
IndividualsAmount = 1000,
UseOptionTypes = true >
let ctx = Sql.GetDataContext()
let agent = MailboxProcessor.Start(fun (inbox:MailboxProcessor<String>) ->
let rec loop() =
async {
let! msg = inbox.Receive()
match msg with
| _ ->
let row = ctx.Main.Events.Create()
row.Timestamp <- DateTime.Now
printfn "Submitting"
ctx.SubmitUpdates()
printfn "Submitted"
return! loop()
}
loop()
)
agent.Post "Hello"
When compiled to an exe, "Submitting" is printed, but then it hangs. If you want to try it out, the full code is on github here
It seems the problem was that the main thread was exiting before the MailboxProcessor could process it's mailbox. FSI is long-lived and so this wasn't happening there. I changed:
[<EntryPoint>]
let main argv =
agent.Post "Hello"
agent.Post "Hello again"
0
to
[<EntryPoint>]
let main argv =
agent.Post "Hello"
agent.Post "Hello again"
let waitLoop = async {
while agent.CurrentQueueLength > 0 do
printfn "Sleeping"
do! Async.Sleep 1000
}
Async.RunSynchronously waitLoop
0
and now the code executes as I had intended.

Is returning results from MailboxProcessor via Rx a good idea?

I am a little curious about the code example below and what people think.
The idea was to read from a NetworkStream (~20 msg/s) and instead of working in the main, pass things to MainboxProcessor to handle and get things back for bindings when done.
The usual way is to use PostAndReply, but I want to bind to ListView or other control in C#. Must do magic with LastN items and filtering anyway.
Plus, Rx has some error handling.
The example below observes numbers from 2..10 and returns "hello X". On 8 it stops like it was EOF. Made it to ToEnumerable because other thread finishes before otherwise, but it works with Subscribe as well.
What bothers me:
passing Subject(obj) around in recursion. I don't see any problems having around 3-4 of those. Good idea?
Lifetime of Subject.
open System
open System.Threading
open System.Reactive.Subjects
open System.Reactive.Linq // NuGet, take System.Reactive.Core also.
open System.Reactive.Concurrency
type SerializedLogger() =
let _letters = new Subject<string>()
// create the mailbox processor
let agent = MailboxProcessor.Start(fun inbox ->
// the message processing function
let rec messageLoop (letters:Subject<string>) = async{
// read a message
let! msg = inbox.Receive()
printfn "mailbox: %d in Thread: %d" msg Thread.CurrentThread.ManagedThreadId
do! Async.Sleep 100
// write it to the log
match msg with
| 8 -> letters.OnCompleted() // like EOF.
| x -> letters.OnNext(sprintf "hello %d" x)
// loop to top
return! messageLoop letters
}
// start the loop
messageLoop _letters
)
// public interface
member this.Log msg = agent.Post msg
member this.Getletters() = _letters.AsObservable()
/// Print line with prefix 1.
let myPrint1 x = printfn "onNext - %s, Thread: %d" x Thread.CurrentThread.ManagedThreadId
// Actions
let onNext = new Action<string>(myPrint1)
let onCompleted = new Action(fun _ -> printfn "Complete")
[<EntryPoint>]
let main argv =
async{
printfn "Main is on: %d" Thread.CurrentThread.ManagedThreadId
// test
let logger = SerializedLogger()
logger.Log 1 // ignored?
let xObs = logger
.Getletters() //.Where( fun x -> x <> "hello 5")
.SubscribeOn(Scheduler.CurrentThread)
.ObserveOn(Scheduler.CurrentThread)
.ToEnumerable() // this
//.Subscribe(onNext, onCompleted) // or with Dispose()
[2..10] |> Seq.iter (logger.Log)
xObs |> Seq.iter myPrint1
while true
do
printfn "waiting"
System.Threading.Thread.Sleep(1000)
return 0
} |> Async.RunSynchronously // return an integer exit code
I have done similar things, but using the plain F# Event type rather than Subject. It basically lets you create IObservable and trigger its subscribes - much like your use of more complex Subject. The event-based version would be:
type SerializedLogger() =
let letterProduced = new Event<string>()
let lettersEnded = new Event<unit>()
let agent = MailboxProcessor.Start(fun inbox ->
let rec messageLoop (letters:Subject<string>) = async {
// Some code omitted
match msg with
| 8 -> lettersEnded.Trigger()
| x -> letterProduced.Trigger(sprintf "hello %d" x)
// ...
member this.Log msg = agent.Post msg
member this.LetterProduced = letterProduced.Publish
member this.LettersEnded = lettersEnded.Publish
The important differences are:
Event cannot trigger OnCompleted, so I instead exposed two separate events. This is quite unfortunate! Given that Subject is very similar to events in all other aspects, this might be a good reason for using subject instead of plain event.
The nice aspect of using Event is that it is a standard F# type, so you do not need any external dependencies in the agent.
I noticed your comment noting that the first call to Log was ignored. That's because you subscribe to the event handler only after this call happens. I think you could use ReplaySubject variation on the Subject idea here - it replays all events when you subscribe to it, so the one that happened earlier would not be lost (but there is a cost to caching).
In summary, I think using Subject is probably a good idea - it is essentially the same pattern as using Event (which I think is quite standard way of exposing notifications from agents), but it lets you trigger OnCompleted. I would probably not use ReplaySubject, because of the caching cost - you just have to make sure to subscribe before triggering any events.

'Last message only' option in ZMQ Subscribe socket

A ZMQ subscriber socket keeps only the last message in queue when the CONFLATE option is set to true. (zmq_docs) However, it does not seem to be working for me. Typically in python i would do something like the following and it would work:
context = zmq.Context()
subscriber = context.socket(zmq.SUB)
subscriber.setsockopt(zmq.CONFLATE, 1)
subscriber.connect("tcp://localhost:5555")
The subscriber in pub/sub pattern below simply ignores the CONFLATE option set to 1. You can observe by the minute and second being displayed on the subscriber that the program is tied up as it is processing old messages (fib 42). If fib is set to a trivial value, you can see that the subscriber is indeed receiving messages from the publisher.
Here is the publisher:
open System
open fszmq
open fszmq.Context
open fszmq.Socket
let funcPublish () =
use context = new Context()
use publisher = pub context
"tcp://*:5563" |> bind publisher
while true do
let tm = System.DateTime.Now
let t = String.Concat([tm.Minute.ToString(); " "; tm.Second.ToString()])
t |> s_send publisher
sleep 1
EXIT_SUCCESS
[<EntryPoint>]
let main argv =
funcPublish ()
0
And here is the subscriber:
open fszmq
open fszmq.Context
open fszmq.Socket
let rec fib n =
match n with
| 1 | 2 -> 1
| n -> fib(n-1) + fib(n-2)
let funcSubscribe () =
use context = new Context()
use subscriber = sub context
"tcp://localhost:5563" |> connect subscriber
Socket.setOption subscriber (ZMQ.CONFLATE, 1)
[ ""B ] |> subscribe subscriber
while true do
let contents = s_recv subscriber
fib 42
contents |> printfn "%A"
EXIT_SUCCESS
[<EntryPoint>]
let main argv =
funcSubscribe ()
0
Thanks.
I noticed one difference between your Python code and your F# code. In Python, you set the CONFLATE option before you connect to the socket, but in F#, you set the CONFLATE option after you connect.
If you move the Socket.setOption line to before your connect subscriber line in your F# code, I suspect that should solve your problem.
In other words, instead of this:
"tcp://localhost:5563" |> connect subscriber
Socket.setOption subscriber (ZMQ.CONFLATE, 1)
you should probably have this:
Socket.setOption subscriber (ZMQ.CONFLATE, 1)
"tcp://localhost:5563" |> connect subscriber

F# wake up program

I have this requirement: my F# program will do daily job at 11:00AM in the morning; I turn on my PC usually on 8:00AM, since I have been busy, so I always forget to run my F# program on time. So, I want to have a function, which can check how many seconds between now and 11:00AM, if there are 3 hours, then my program will sleep 10800 seconds, then wake up and do the job. I know I can use Windows task scheduler for this kind of job, but this way, I will not see the output from my F# program, so I have to do this in my way:
let wakeup() =
let today = DateTime.Today.ToShortDateString()
let beignTime = DateTime.Parse(today + " 11:00:00")
// Don’t know what to do yet!
For function wakeup(), I want to return an int32 number of seconds if the current time is before beginTime, if current time is later than beginTime, just return 0.
let seconds2Go = wakeup()
if (seconds2Go > 0) then
Thread.Sleep(seconds2Go * 1000)
else
printfn "Do daily job!"
But I don’t have a good idea on how to write the function wakeup().
Please offer your help.
Thanks,
John
This does the trick. You'll have to stop it with CTRL+C.
open System
open System.Threading
let (|TimeSpan|_|) value =
match TimeSpan.TryParse(value) with
| true, t -> Some t
| _ -> None
let runDaily time f =
let time = ref <| DateTime.Today.Add(time)
let rec loop() =
async {
if DateTime.Now >= !time then
time := (!time).AddDays(1.0)
f()
else do! Async.Sleep(1000)
return! loop()
}
use cts = new CancellationTokenSource()
Console.CancelKeyPress.Add(fun args -> cts.Cancel(); args.Cancel <- true)
try Async.RunSynchronously(loop(), cancellationToken = cts.Token)
with :? OperationCanceledException -> ()
[<EntryPoint>]
let main args =
match args with
| [|TimeSpan time|] ->
runDaily time (fun () ->
//TODO: program logic
)
0
| _ -> eprintfn "Usage: program.exe time"; 1
Usage
program.exe 11:00 //run every day at 11AM
let wakeup () =
let beginTime = DateTime.Today + TimeSpan(11,0,0)
beginTime.Subtract(DateTime.Now).TotalSeconds |> int
Why don't you jut use the Windows Scheuler to run the program at 11:00?
let wakeup () = 11.0*60.0*60.0 - (DateTime.Now - DateTime.Today).TotalSeconds |> int

Resources