Session Scheduler Sample
Source code
using System;
using OnixS.Fix;
using OnixS.Fix.Scheduling;
namespace SessionScheduler
{
/// <summary>
/// This sample demonstrates the use of scheduling services and the ability to define session sequence number reset policy.
/// </summary>
sealed class Sample : IDisposable
{
private Scheduler scheduler;
private Session acceptor;
private Session initiator;
public Sample()
{
ConstructScheduler();
ConstructSessions();
}
private void ConstructSessions()
{
const string AcceptorId = "Acceptor";
const string InitiatorId = "Initiator";
const ProtocolVersion protocolVersion = ProtocolVersion.Fix44;
const bool seqNumberLogonPolicy = true;
acceptor = new Session(AcceptorId, InitiatorId, protocolVersion, seqNumberLogonPolicy, SessionStorageType.FileBasedStorage);
acceptor.Error += (sender, args) =>
{
Log("Session Error: " + args.ToString());
};
acceptor.Warning += (sender, args) =>
{
Log("Session Warning: " + args.ToString());
};
initiator = new Session(InitiatorId, AcceptorId, protocolVersion, seqNumberLogonPolicy, SessionStorageType.FileBasedStorage);
initiator.Error += (sender, args) =>
{
Log("Session Error: " + args.ToString());
};
initiator.Warning += (sender, args) =>
{
Log("Session Warning: " + args.ToString());
};
// The sample schedules the session - initiator, so its state will be traced to make sure the scheduler works.
initiator.StateChanged += (sender, args) =>
{
Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Session {sender} changed its state from {args.PrevState} to {args.NewState}.");
};
}
private void ConstructScheduler()
{
const string ConfigFile = "Scheduler.config";
scheduler = new Scheduler(ConfigFile);
scheduler.Error += (object scheduler, OnixS.Fix.Scheduling.SessionErrorEventArgs args) => {
Log($"Scheduler reported error for session {args.Session}: {args.Reason}");
};
}
~Sample()
{
CleanUp();
}
public void Dispose()
{
CleanUp();
GC.SuppressFinalize(this);
}
private void CleanUp()
{
scheduler.Dispose();
initiator.Dispose();
acceptor.Dispose();
}
public void Run()
{
// The acceptor is maintained manually. So the reset of sequence numbers must be done manually before logon.
acceptor.ResetLocalSequenceNumbers();
// Comment this call if you want to check how scheduler notifies about errors.
acceptor.LogonAsAcceptor();
SessionSchedule scheduleForInitiator = ConstructShortTimeActivitySchedule();
scheduler.Register(initiator, scheduleForInitiator, scheduler.ConnectionSettings["LocalInitiator"]);
Log($"{initiator} is scheduled for automatic connection.");
// After the construction, the session is in a disconnected state, so we cannot wait for logout.
// Instead, we have to give time to the scheduler to activate the session.
// Let's wait till the scheduled logout time.
PauseUntil(scheduleForInitiator.LogoutTimes[(int)DateTime.Now.DayOfWeek]);
// Make sure that the initiator is shutdown.
WaitUntilLogout(initiator);
// Once the initiator disconnects from the acceptor, the acceptor starts waiting for the next login.
// So we have to logout the acceptor manually.
acceptor.Logout();
WaitUntilLogout(acceptor);
}
private static SessionSchedule ConstructShortTimeActivitySchedule()
{
const int LogonDelayInSeconds = 5;
Log($"LogonDelayInSeconds={LogonDelayInSeconds}");
TimeSpan logonTime = DateTime.Now.TimeOfDay.Add(new TimeSpan(0, 0, LogonDelayInSeconds));
const int SessionDurationInSeconds = 10;
Log($"SessionDurationInSeconds={SessionDurationInSeconds}");
TimeSpan logoutTime = logonTime.Add(new TimeSpan(0, 0, SessionDurationInSeconds));
return new SessionSchedule(
DayOfWeek.Sunday,
DayOfWeek.Saturday,
logonTime,
logoutTime,
SessionDuration.Day,
SequenceNumberResetPolicy.Daily);
}
private static void PauseUntil(TimeSpan timeOfDay)
{
System.Threading.Thread.Sleep(timeOfDay.Subtract(DateTime.Now.TimeOfDay));
}
private static void WaitUntilLogout(Session session)
{
while (session.State != SessionState.Disconnected)
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1));
}
private static void Log(string msg)
{
Console.Out.WriteLine($"{DateTime.Now.ToLongTimeString()}: {msg}");
}
}
}