Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion session.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ type session struct {
transportDataDictionary *datadictionary.DataDictionary
appDataDictionary *datadictionary.DataDictionary

timestampPrecision TimestampPrecision
timestampPrecision TimestampPrecision
lastCheckedResetSeqTime time.Time
}

func (s *session) logError(err error) {
Expand Down
26 changes: 20 additions & 6 deletions session_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,27 @@ func (sm *stateMachine) CheckSessionTime(session *session, now time.Time) {
}

func (sm *stateMachine) CheckResetTime(session *session, now time.Time) {
if session.EnableResetSeqTime {
if session.ResetSeqTime.Hour() == now.Hour() &&
session.ResetSeqTime.Minute() == now.Minute() &&
session.ResetSeqTime.Second() == now.Second() {
session.sendLogonInReplyTo(true, nil)
}
// If the reset time is not enabled, we do nothing.
if !session.EnableResetSeqTime {
return
}
// If the last checked reset seq time is not set or we are not connected, we do nothing.
if session.lastCheckedResetSeqTime.IsZero() || !session.stateMachine.State.IsConnected() {
session.lastCheckedResetSeqTime = now
return
}

// Get the reset time for today
nowInTimeZone := now.In(session.ResetSeqTime.Location())
resetSeqTimeToday := time.Date(nowInTimeZone.Year(), nowInTimeZone.Month(), nowInTimeZone.Day(), session.ResetSeqTime.Hour(), session.ResetSeqTime.Minute(), session.ResetSeqTime.Second(), session.ResetSeqTime.Nanosecond(), session.ResetSeqTime.Location())

// If we have crossed the reset time boundary in between checks or we are at the reset time, we send the reset
if session.lastCheckedResetSeqTime.Before(resetSeqTimeToday) && !now.Before(resetSeqTimeToday) {
session.sendLogonInReplyTo(true, nil)
}

// Update the last checked reset seq time to now
session.lastCheckedResetSeqTime = now
}

func (sm *stateMachine) setState(session *session, nextState sessionState) {
Expand Down
278 changes: 276 additions & 2 deletions session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1010,20 +1010,24 @@ func (s *SessionSuite) TestSeqNumResetTime() {
s.session.ResetSeqTime = now
s.session.EnableResetSeqTime = true

s.NextSenderMsgSeqNum(1)
s.NextTargetMsgSeqNum(1)
s.IncrNextSenderMsgSeqNum()
s.IncrNextTargetMsgSeqNum()

s.MockApp.On("ToAdmin")

s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)
s.IncrNextSenderMsgSeqNum()
s.IncrNextTargetMsgSeqNum()

s.MockApp.On("ToAdmin")

s.session.CheckResetTime(s.session, now)

s.NextSenderMsgSeqNum(2)
s.NextSenderMsgSeqNum(2)
s.NextSenderMsgSeqNum(3)
s.NextSenderMsgSeqNum(3)

}

Expand Down Expand Up @@ -1052,3 +1056,273 @@ func (s *SessionSuite) TestSeqNumResetTimeDisconnected() {
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)
}

func (s *SessionSuite) TestSeqNumResetTimeAfterElapse() {
s.session.State = logonState{}
before := time.Now().UTC()
resetTime := time.Now().UTC().Add(time.Second * 2)
after := resetTime.Add(time.Second * 1)
s.session.ResetSeqTime = resetTime
s.session.EnableResetSeqTime = true

s.NextSenderMsgSeqNum(1)
s.NextTargetMsgSeqNum(1)
s.IncrNextTargetMsgSeqNum()
s.IncrNextSenderMsgSeqNum()
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.MockApp.On("ToAdmin")
s.session.CheckResetTime(s.session, before)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.session.lastCheckedResetSeqTime = before
s.session.CheckResetTime(s.session, after)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(1)
}

func (s *SessionSuite) TestSeqNumResetTimeNotAfterDisconnect() {
s.session.State = logonState{}
before := time.Now().UTC()
resetTime := before.Add(time.Second * 2)
after := resetTime.Add(time.Second * 1)
s.session.ResetSeqTime = resetTime
s.session.EnableResetSeqTime = true

s.NextSenderMsgSeqNum(1)
s.NextTargetMsgSeqNum(1)
s.IncrNextTargetMsgSeqNum()
s.IncrNextSenderMsgSeqNum()
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.MockApp.On("ToAdmin")
s.session.CheckResetTime(s.session, before)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.session.onAdmin(stopReq{})
s.Disconnected()
s.Stopped()

s.session.lastCheckedResetSeqTime = before
s.session.CheckResetTime(s.session, after)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)
}

func (s *SessionSuite) TestSeqNumResetTimeDisconnected_LocalTZ() {
s.session.State = logonState{}
tz, err := time.LoadLocation("America/Chicago")
if err != nil {
s.T().Fatal(err)
}
s.session.TimeZone = tz
s.session.ResetSeqTime = time.Now().In(tz).Add(time.Second * 2)
s.session.EnableResetSeqTime = true

s.NextSenderMsgSeqNum(1)
s.NextTargetMsgSeqNum(1)
s.IncrNextTargetMsgSeqNum()
s.IncrNextSenderMsgSeqNum()
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.session.onAdmin(stopReq{})
s.Disconnected()
s.Stopped()

// Wait for reset time to pass.
time.Sleep(time.Second * 3)

s.MockApp.On("ToAdmin")
// Disconnected so the seq numbers should not be reset.
s.session.CheckResetTime(s.session, time.Now().UTC())
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)
}

func (s *SessionSuite) TestSeqNumResetTimeAfterElapse_LocalTZ() {
s.session.State = logonState{}
tz, err := time.LoadLocation("America/Chicago")
if err != nil {
s.T().Fatal(err)
}
s.session.TimeZone = tz
before := time.Now().In(tz)
resetTime := before.Add(time.Second * 2)
after := resetTime.Add(time.Second * 1)
s.session.ResetSeqTime = resetTime
s.session.EnableResetSeqTime = true

s.NextSenderMsgSeqNum(1)
s.NextTargetMsgSeqNum(1)
s.IncrNextTargetMsgSeqNum()
s.IncrNextSenderMsgSeqNum()
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.MockApp.On("ToAdmin")
s.session.CheckResetTime(s.session, before)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.session.lastCheckedResetSeqTime = before
s.session.CheckResetTime(s.session, after)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(1)
}

func (s *SessionSuite) TestSeqNumResetTimeNotAfterDisconnect_LocalTZ() {
s.session.State = logonState{}
tz, err := time.LoadLocation("America/Chicago")
if err != nil {
s.T().Fatal(err)
}
s.session.TimeZone = tz
before := time.Now().In(tz)
resetTime := before.Add(time.Second * 2)
after := resetTime.Add(time.Second * 1)
s.session.ResetSeqTime = resetTime
s.session.EnableResetSeqTime = true

s.NextSenderMsgSeqNum(1)
s.NextTargetMsgSeqNum(1)
s.IncrNextTargetMsgSeqNum()
s.IncrNextSenderMsgSeqNum()
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.MockApp.On("ToAdmin")
s.session.CheckResetTime(s.session, before)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.session.onAdmin(stopReq{})
s.Disconnected()
s.Stopped()

s.session.lastCheckedResetSeqTime = before
s.session.CheckResetTime(s.session, after)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)
}

func (s *SessionSuite) TestSeqNumResetTimeAfterElapse_MultipleDaysLater() {
s.session.State = logonState{}

// Example Dates:
// ResetTime = 2025-06-08 10:15:30
// Before = 2025-07-10 10:15:29
// After = 2025-07-10 10:15:32
resetTime := time.Now()
before := resetTime.AddDate(0, 1, 2).Add(time.Second * -1)
after := resetTime.AddDate(0, 1, 2).Add(time.Second * 2)

s.session.ResetSeqTime = resetTime
s.session.EnableResetSeqTime = true

s.NextSenderMsgSeqNum(1)
s.NextTargetMsgSeqNum(1)
s.IncrNextTargetMsgSeqNum()
s.IncrNextSenderMsgSeqNum()
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.MockApp.On("ToAdmin")
s.session.CheckResetTime(s.session, before)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.session.lastCheckedResetSeqTime = before
s.session.CheckResetTime(s.session, after)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(1)
}

func (s *SessionSuite) TestSeqNumResetTimeAtExactTime() {
s.session.State = logonState{}
resetTime := time.Now()
before := resetTime.Add(time.Second * -1)

s.session.ResetSeqTime = resetTime
s.session.EnableResetSeqTime = true

s.NextSenderMsgSeqNum(1)
s.NextTargetMsgSeqNum(1)
s.IncrNextTargetMsgSeqNum()
s.IncrNextSenderMsgSeqNum()
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

// We are before the reset time, so we should not reset the seq numbers
s.MockApp.On("ToAdmin")
s.session.CheckResetTime(s.session, before)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

// We check the reset time exactly at the configured time, so we reset
s.session.lastCheckedResetSeqTime = before
s.session.CheckResetTime(s.session, resetTime)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(1)
}

func (s *SessionSuite) TestSeqNumResetTimePreviousCheck() {
s.session.State = logonState{}
resetTime := time.Now()
before := resetTime.Add(time.Second * -1)
after := resetTime.Add(time.Second * 1)

s.session.ResetSeqTime = resetTime
s.session.EnableResetSeqTime = true

s.NextSenderMsgSeqNum(1)
s.NextTargetMsgSeqNum(1)
s.IncrNextTargetMsgSeqNum()
s.IncrNextSenderMsgSeqNum()
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

// We are before the reset time, so we should not reset the seq numbers
s.MockApp.On("ToAdmin")
s.session.CheckResetTime(s.session, before)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

// We just checked the reset time at the configured time, so we do not reset the seq num for the next check
s.session.lastCheckedResetSeqTime = resetTime
s.session.CheckResetTime(s.session, after)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)
}

func (s *SessionSuite) TestSeqNumResetTime_NanoSeconds() {
s.session.State = logonState{}
resetTime := time.Now()
// Nanoseconds are not used in the resetSeqTime, but are referenced in the comparison of before/after
before := resetTime.Add(time.Nanosecond * -1)
after := resetTime.Add(time.Nanosecond * 1)

s.session.ResetSeqTime = resetTime
s.session.EnableResetSeqTime = true

s.NextSenderMsgSeqNum(1)
s.NextTargetMsgSeqNum(1)
s.IncrNextTargetMsgSeqNum()
s.IncrNextSenderMsgSeqNum()
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.MockApp.On("ToAdmin")
s.session.CheckResetTime(s.session, before)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(2)

s.session.lastCheckedResetSeqTime = before
s.session.CheckResetTime(s.session, after)
s.NextSenderMsgSeqNum(2)
s.NextTargetMsgSeqNum(1)
}
Loading