model/update_manager.go
package model
import (
"bitbucket.org/enroute-mobi/ara/clock"
"bitbucket.org/enroute-mobi/ara/logger"
"bitbucket.org/enroute-mobi/ara/siri/siri_attributes"
"bitbucket.org/enroute-mobi/ara/uuid"
"golang.org/x/exp/maps"
)
type UpdateManager struct {
clock.ClockConsumer
uuid.UUIDConsumer
model Model
}
func NewUpdateManager(model Model) func(UpdateEvent) {
manager := newUpdateManager(model)
return manager.Update
}
// Test method
func newUpdateManager(model Model) *UpdateManager {
return &UpdateManager{model: model}
}
func (manager *UpdateManager) Update(event UpdateEvent) {
switch event.EventKind() {
case STOP_AREA_EVENT:
manager.updateStopArea(event.(*StopAreaUpdateEvent))
case LINE_EVENT:
manager.updateLine(event.(*LineUpdateEvent))
case VEHICLE_JOURNEY_EVENT:
manager.updateVehicleJourney(event.(*VehicleJourneyUpdateEvent))
case STOP_VISIT_EVENT:
manager.updateStopVisit(event.(*StopVisitUpdateEvent))
case VEHICLE_EVENT:
manager.updateVehicle(event.(*VehicleUpdateEvent))
case STATUS_EVENT:
manager.updateStatus(event.(*StatusUpdateEvent))
case NOT_COLLECTED_EVENT:
manager.updateNotCollected(event.(*NotCollectedUpdateEvent))
case SITUATION_EVENT:
manager.updateSituation(event.(*SituationUpdateEvent))
}
}
func (manager *UpdateManager) updateSituation(event *SituationUpdateEvent) {
situation, ok := manager.model.Situations().FindByCode(event.SituationCode)
if ok &&
situation.RecordedAt == event.RecordedAt &&
situation.Version == event.Version {
return
}
if !ok {
situation = manager.model.Situations().New()
situation.Origin = event.Origin
situation.SetCode(event.SituationCode)
situation.SetCode(NewCode(Default, event.SituationCode.HashValue()))
}
situation.RecordedAt = event.RecordedAt
situation.Version = event.Version
situation.ProducerRef = event.ProducerRef
situation.ParticipantRef = event.ParticipantRef
situation.InternalTags = event.InternalTags
situation.Summary = event.Summary
situation.Description = event.Description
situation.VersionedAt = event.VersionedAt
situation.ValidityPeriods = event.ValidityPeriods
situation.PublicationWindows = event.PublicationWindows
situation.Keywords = event.Keywords
situation.ReportType = event.ReportType
situation.AlertCause = event.AlertCause
situation.Severity = event.Severity
situation.Progress = event.Progress
situation.Reality = event.Reality
situation.Format = event.Format
situation.Affects = event.Affects
situation.Consequences = event.Consequences
situation.PublishToWebActions = event.PublishToWebActions
situation.PublishToMobileActions = event.PublishToMobileActions
situation.PublishToDisplayActions = event.PublishToDisplayActions
// Default is AfterCreate
var h hook
if ok {
h = AfterSave
}
macros := manager.model.Macros().GetMacros(h, MacroSituationType)
for i := range macros {
macros[i].Update(&situation)
}
situation.Save()
}
func (manager *UpdateManager) updateStopArea(event *StopAreaUpdateEvent) {
if event.Code.Value() == "" { // Avoid creating a StopArea with an empty code
return
}
stopArea, found := manager.model.StopAreas().FindByCode(event.Code)
if !found {
stopArea = manager.model.StopAreas().New()
stopArea.SetCode(event.Code)
stopArea.CollectSituations = true
stopArea.Name = event.Name
stopArea.CollectedAlways = event.CollectedAlways
stopArea.Longitude = event.Longitude
stopArea.Latitude = event.Latitude
}
if stopArea.ParentId == "" && event.ParentCode.Value() != "" {
parentSA, _ := manager.model.StopAreas().FindByCode(event.ParentCode)
stopArea.ParentId = parentSA.Id()
}
stopArea.Updated(manager.Clock().Now())
// Default is AfterCreate
var h hook
if found {
h = AfterSave
}
macros := manager.model.Macros().GetMacros(h, StopAreaType)
for i := range macros {
macros[i].Update(stopArea)
}
manager.model.StopAreas().Save(stopArea)
if event.Origin != "" {
status, ok := stopArea.Origins.Origin(event.Origin)
if !status || !ok {
manager.updateStatus(NewStatusUpdateEvent(stopArea.Id(), event.Origin, true))
}
}
}
func (manager *UpdateManager) updateMonitoredStopArea(stopAreaId StopAreaId, partner string, status bool) {
ascendants := manager.model.StopAreas().FindAscendants(stopAreaId)
for i := range ascendants {
stopArea := ascendants[i]
stopArea.SetPartnerStatus(partner, status)
manager.model.StopAreas().Save(stopArea)
}
}
func (manager *UpdateManager) updateLine(event *LineUpdateEvent) {
if event.Code.Value() == "" { // Avoid creating a Line with an empty code
return
}
line, found := manager.model.Lines().FindByCode(event.Code)
if !found {
line = manager.model.Lines().New()
line.SetCode(event.Code)
line.SetCode(NewCode(Default, event.Code.HashValue()))
line.CollectSituations = true
line.Name = event.Name
line.SetOrigin(event.Origin)
}
line.Updated(manager.Clock().Now())
// Default is AfterCreate
var h hook
if found {
h = AfterSave
}
macros := manager.model.Macros().GetMacros(h, LineType)
for i := range macros {
macros[i].Update(line)
}
manager.model.Lines().Save(line)
}
func (manager *UpdateManager) updateVehicleJourney(event *VehicleJourneyUpdateEvent) {
if event.Code.Value() == "" { // Avoid creating a VehicleJourney with an empty code
return
}
vj, found := manager.model.VehicleJourneys().FindByCode(event.Code)
if !found {
// LineCode
l, ok := manager.model.Lines().FindByCode(event.LineCode)
if !ok {
logger.Log.Debugf("VehicleJourney update event without corresponding line: %v", event.LineCode.String())
return
}
vj = manager.model.VehicleJourneys().New()
vj.SetCode(event.Code)
vj.SetCode(NewCode(Default, event.Code.HashValue()))
vj.Origin = event.Origin
vj.Name = event.Attributes()[siri_attributes.VehicleJourneyName]
vj.LineId = l.Id()
}
maps.Copy(vj.Attributes, event.Attributes())
if vj.References.IsEmpty() {
vj.References = event.References()
}
vj.References.SetCode("OriginRef", NewCode(event.Code.CodeSpace(), event.OriginRef))
vj.OriginName = event.OriginName
vj.References.SetCode("DestinationRef", NewCode(event.Code.CodeSpace(), event.DestinationRef))
vj.DestinationName = event.DestinationName
if event.Direction != "" { // Only used for Push collector
vj.Attributes.Set(siri_attributes.DirectionName, event.Direction)
}
if event.Occupancy != Undefined {
vj.Occupancy = event.Occupancy
}
vj.Monitored = event.Monitored
if event.DirectionType != "" { // Do not override unknown DirectionType
vj.DirectionType = event.DirectionType
}
// Default is AfterCreate
var h hook
if found {
h = AfterSave
}
macros := manager.model.Macros().GetMacros(h, VehicleJourneyType)
for i := range macros {
macros[i].Update(vj)
}
manager.model.VehicleJourneys().Save(vj)
}
func (manager *UpdateManager) updateStopVisit(event *StopVisitUpdateEvent) {
if event.Code.Value() == "" { // Avoid creating a StopVisit with an empty code
return
}
vj, ok := manager.model.VehicleJourneys().FindByCode(event.VehicleJourneyCode)
if !ok {
logger.Log.Debugf("StopVisit update event without corresponding vehicle journey: %v", event.VehicleJourneyCode.String())
return
}
var sa *StopArea
var sv *StopVisit
var found bool
if event.StopAreaCode.Value() == "" {
sv, found = manager.model.StopVisits().FindByCode(event.Code)
if !found {
logger.Log.Debugf("Can't find Stopvisit from update event without stop area id")
return
}
sa = sv.StopArea()
if sa == nil {
logger.Log.Printf("StopVisit in memory without a StopArea: %v", sv.Id())
return
}
} else {
sa, ok = manager.model.StopAreas().FindByCode(event.StopAreaCode)
if !ok {
logger.Log.Debugf("StopVisit update event without corresponding stop area: %v", event.StopAreaCode.String())
return
}
sv, found = manager.model.StopVisits().FindByCode(event.Code)
if !found {
sv = manager.model.StopVisits().New()
sv.SetCode(event.Code)
sv.SetCode(NewCode(Default, event.Code.HashValue()))
sv.StopAreaId = sa.Id()
sv.VehicleJourneyId = vj.Id()
sv.Origin = event.Origin
sv.PassageOrder = event.PassageOrder
sv.DataFrameRef = event.DataFrameRef
}
}
if sv.Attributes.IsEmpty() {
sv.Attributes = event.Attributes()
}
if sv.References.IsEmpty() {
sv.References = event.References()
}
// Update StopArea Lines
l := vj.Line()
if l != nil {
sa.LineIds.Add(l.Id())
referent, ok := manager.model.StopAreas().Find(sa.ReferentId)
if ok {
referent.LineIds.Add(l.Id())
manager.model.StopAreas().Save(referent)
}
manager.model.StopAreas().Save(sa)
}
if !event.RecordedAt.IsZero() {
sv.RecordedAt = event.RecordedAt
} else if !sv.Schedules.Include(event.Schedules) {
sv.RecordedAt = manager.Clock().Now()
}
sv.Schedules.Merge(event.Schedules)
if event.DepartureStatus != "" {
sv.DepartureStatus = event.DepartureStatus
}
if event.ArrivalStatus != "" {
sv.ArrivalStatus = event.ArrivalStatus
}
sv.VehicleAtStop = event.VehicleAtStop
sv.Collected(manager.Clock().Now())
if event.Monitored != vj.Monitored {
vj.Monitored = event.Monitored
manager.model.VehicleJourneys().Save(vj)
}
if event.Origin != "" {
status, ok := sa.Origins.Origin(event.Origin)
if status != event.Monitored || !ok {
manager.updateMonitoredStopArea(sa.Id(), event.Origin, event.Monitored)
}
}
// Default is AfterCreate
var h hook
if found {
h = AfterSave
}
macros := manager.model.Macros().GetMacros(h, StopVisitType)
for i := range macros {
macros[i].Update(sv)
}
manager.model.StopVisits().Save(sv)
// VehicleJourney stop sequence
if !vj.HasCompleteStopSequence {
completeStopSequence := vj.model.ScheduledStopVisits().StopVisitsLenByVehicleJourney(vj.Id()) == vj.model.StopVisits().StopVisitsLenByVehicleJourney(vj.Id())
if completeStopSequence {
vj.HasCompleteStopSequence = true
manager.model.VehicleJourneys().Save(vj)
}
}
// long term historisation
if sv.IsArchivable() {
sva := &StopVisitArchiver{
Model: manager.model,
StopVisit: sv,
}
sva.Archive()
}
}
func (manager *UpdateManager) updateVehicle(event *VehicleUpdateEvent) {
sa, _ := manager.model.StopAreas().FindByCode(event.StopAreaCode)
vj, _ := manager.model.VehicleJourneys().FindByCode(event.VehicleJourneyCode)
line := vj.Line()
vehicle, found := manager.model.Vehicles().FindByCode(event.Code)
if !found {
vehicle = manager.model.Vehicles().New()
vehicle.SetCode(event.Code)
}
if event.NextStopPointOrder != 0 {
if sv := manager.model.StopVisits().FindByVehicleJourneyIdAndStopVisitOrder(VehicleJourneyId(vj.Id()), event.NextStopPointOrder); sv != nil {
vehicle.NextStopVisitId = sv.Id()
}
} else {
if sa != nil {
svIds := manager.model.StopVisits().FindByVehicleJourneyIdAndStopAreaId(VehicleJourneyId(vj.Id()), StopAreaId(sa.Id()))
if len(svIds) == 1 {
vehicle.NextStopVisitId = svIds[0]
}
}
}
vehicle.StopAreaId = sa.Id()
vehicle.VehicleJourneyId = vj.Id()
vehicle.DriverRef = event.DriverRef
vehicle.Longitude = event.Longitude
vehicle.Latitude = event.Latitude
vehicle.Bearing = event.Bearing
vehicle.LinkDistance = event.LinkDistance
vehicle.Percentage = event.Percentage
vehicle.ValidUntilTime = event.ValidUntilTime
if event.RecordedAt.IsZero() {
vehicle.RecordedAtTime = manager.Clock().Now()
} else {
vehicle.RecordedAtTime = event.RecordedAt
}
if event.Occupancy != Undefined {
vehicle.Occupancy = event.Occupancy
}
if line != nil {
vehicle.LineId = line.Id()
}
// Default is AfterCreate
var h hook
if found {
h = AfterSave
}
macros := manager.model.Macros().GetMacros(h, VehicleType)
for i := range macros {
macros[i].Update(vehicle)
}
manager.model.Vehicles().Save(vehicle)
}
func (manager *UpdateManager) updateStatus(event *StatusUpdateEvent) {
ascendants := manager.model.StopAreas().FindAscendants(event.StopAreaId)
for i := range ascendants {
stopArea := ascendants[i]
stopArea.SetPartnerStatus(event.Partner, event.Status)
manager.model.StopAreas().Save(stopArea)
}
}
func (manager *UpdateManager) updateNotCollected(event *NotCollectedUpdateEvent) {
stopVisit, found := manager.model.StopVisits().FindByCode(event.Code)
if !found {
logger.Log.Debugf("StopVisitNotCollectedEvent on unknown StopVisit: %#v", event)
return
}
stopVisit.NotCollected(event.NotCollectedAt)
manager.model.StopVisits().Save(stopVisit)
if stopVisit.IsArchivable() {
sva := &StopVisitArchiver{
Model: manager.model,
StopVisit: stopVisit,
}
sva.Archive()
}
logger.Log.Debugf("StopVisit not Collected: %s (%v)", stopVisit.Id(), event.Code)
}