realtime.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // SPDX-FileCopyrightText: Adam Evyčędo
  2. //
  3. // SPDX-License-Identifier: AGPL-3.0-or-later
  4. package traffic
  5. import (
  6. pb "apiote.xyz/p/szczanieckiej/gtfs_rt/transit_realtime"
  7. "log"
  8. "sync"
  9. "time"
  10. "golang.org/x/text/language"
  11. )
  12. type BlockingError struct {
  13. cause error
  14. }
  15. func (e BlockingError) Error() string {
  16. return e.cause.Error()
  17. }
  18. type Alerts struct {
  19. ByLine map[string][]uint
  20. ByTrip map[string][]uint
  21. ByLineType map[LineType][]uint
  22. ByStop map[string][]uint
  23. ByAgency map[string][]uint
  24. Alerts []Alert
  25. }
  26. type SpecificAlert struct {
  27. Header string
  28. Description string
  29. URL string
  30. Cause AlertCause
  31. Effect AlertEffect
  32. }
  33. type Alert struct {
  34. TimeRanges [][2]time.Time
  35. Headers map[language.Tag]string
  36. Descriptions map[language.Tag]string
  37. URLs map[language.Tag]string
  38. Cause AlertCause
  39. Effect AlertEffect
  40. }
  41. type AlertCause uint
  42. const (
  43. CAUSE_UNKNOWN AlertCause = 0
  44. CAUSE_OTHER AlertCause = 1
  45. CAUSE_TECHNICAL_PROBLEM AlertCause = 2
  46. CAUSE_STRIKE AlertCause = 3
  47. CAUSE_DEMONSTRATION AlertCause = 4
  48. CAUSE_ACCIDENT AlertCause = 5
  49. CAUSE_HOLIDAY AlertCause = 6
  50. CAUSE_WEATHER AlertCause = 7
  51. CAUSE_MAINTENANCE AlertCause = 8
  52. CAUSE_CONSTRUCTION AlertCause = 9
  53. CAUSE_POLICE_ACTIVITY AlertCause = 10
  54. CAUSE_MEDICAL_EMERGENCY AlertCause = 11
  55. )
  56. func alertCauseOfGtfs(v *pb.Alert_Cause) AlertCause {
  57. switch v {
  58. case pb.Alert_UNKNOWN_CAUSE.Enum():
  59. return CAUSE_UNKNOWN
  60. case pb.Alert_OTHER_CAUSE.Enum():
  61. return CAUSE_OTHER
  62. case pb.Alert_TECHNICAL_PROBLEM.Enum():
  63. return CAUSE_TECHNICAL_PROBLEM
  64. case pb.Alert_STRIKE.Enum():
  65. return CAUSE_STRIKE
  66. case pb.Alert_DEMONSTRATION.Enum():
  67. return CAUSE_DEMONSTRATION
  68. case pb.Alert_ACCIDENT.Enum():
  69. return CAUSE_ACCIDENT
  70. case pb.Alert_HOLIDAY.Enum():
  71. return CAUSE_HOLIDAY
  72. case pb.Alert_WEATHER.Enum():
  73. return CAUSE_WEATHER
  74. case pb.Alert_MAINTENANCE.Enum():
  75. return CAUSE_MAINTENANCE
  76. case pb.Alert_CONSTRUCTION.Enum():
  77. return CAUSE_CONSTRUCTION
  78. case pb.Alert_POLICE_ACTIVITY.Enum():
  79. return CAUSE_POLICE_ACTIVITY
  80. case pb.Alert_MEDICAL_EMERGENCY.Enum():
  81. return CAUSE_MEDICAL_EMERGENCY
  82. default:
  83. return CAUSE_UNKNOWN
  84. }
  85. }
  86. type AlertEffect uint
  87. const (
  88. EFFECT_UNKNOWN AlertEffect = 0
  89. EFFECT_OTHER AlertEffect = 1
  90. EFFECT_NO_SERVICE AlertEffect = 2
  91. EFFECT_REDUCED_SERVICE AlertEffect = 3
  92. EFFECT_SIGNIFICANT_DELAYS AlertEffect = 4
  93. EFFECT_DETOUR AlertEffect = 5
  94. EFFECT_ADDITIONAL_SERVICE AlertEffect = 6
  95. EFFECT_MODIFIED_SERVICE AlertEffect = 7
  96. EFFECT_STOP_MOVED AlertEffect = 8
  97. EFFECT_NONE AlertEffect = 9
  98. EFFECT_ACCESSIBILITY_ISSUE AlertEffect = 10
  99. )
  100. func alertEffectOfGtfs(v *pb.Alert_Effect) AlertEffect {
  101. switch v {
  102. case pb.Alert_UNKNOWN_EFFECT.Enum():
  103. return EFFECT_UNKNOWN
  104. case pb.Alert_OTHER_EFFECT.Enum():
  105. return EFFECT_OTHER
  106. case pb.Alert_NO_SERVICE.Enum():
  107. return EFFECT_NO_SERVICE
  108. case pb.Alert_REDUCED_SERVICE.Enum():
  109. return EFFECT_REDUCED_SERVICE
  110. case pb.Alert_SIGNIFICANT_DELAYS.Enum():
  111. return EFFECT_SIGNIFICANT_DELAYS
  112. case pb.Alert_DETOUR.Enum():
  113. return EFFECT_DETOUR
  114. case pb.Alert_ADDITIONAL_SERVICE.Enum():
  115. return EFFECT_ADDITIONAL_SERVICE
  116. case pb.Alert_MODIFIED_SERVICE.Enum():
  117. return EFFECT_MODIFIED_SERVICE
  118. case pb.Alert_STOP_MOVED.Enum():
  119. return EFFECT_STOP_MOVED
  120. case pb.Alert_NO_EFFECT.Enum():
  121. return EFFECT_NONE
  122. case pb.Alert_ACCESSIBILITY_ISSUE.Enum():
  123. return EFFECT_ACCESSIBILITY_ISSUE
  124. default:
  125. return EFFECT_UNKNOWN
  126. }
  127. }
  128. // ............ feedID trip/stop
  129. var updates map[string]map[string][]Update
  130. var alerts map[string]Alerts
  131. var vehicleStatuses map[string]map[string]VehicleStatus
  132. var cacheMx sync.Mutex
  133. func GetAlerts(stopID, stopCode string, tripOffset int, ctx Context, t *Traffic, languages []language.Tag) []SpecificAlert {
  134. feedInfo, err := getFeedInfo(ctx.DataHome, ctx.FeedID, ctx.Version)
  135. if err != nil {
  136. log.Printf("while getting feedInfo: %v\n", err)
  137. feedInfo = FeedInfo{}
  138. }
  139. var function func(string, string, string, Context, *Traffic) ([]Alert, error)
  140. if feedInfo.Name != "" {
  141. if _, ok := feedInfo.RealtimeFeeds[ALERTS]; ok {
  142. function = getGtfsRealtimeAlerts
  143. } else if isLuaAlertsScript(ctx) {
  144. function = getLuaRealtimeAlerts
  145. } else {
  146. return []SpecificAlert{}
  147. }
  148. }
  149. tripID := ""
  150. if tripOffset > 0 {
  151. file, err := openTrips(ctx)
  152. if err != nil {
  153. log.Printf("while opening trips: %v\n", err)
  154. return []SpecificAlert{}
  155. }
  156. defer file.Close()
  157. trip, err := GetTripByOffset(file, uint(tripOffset), ctx)
  158. if err != nil {
  159. log.Printf("while getting trip: %v\n", err)
  160. return []SpecificAlert{}
  161. }
  162. tripID = trip.Id
  163. }
  164. if function != nil {
  165. alerts, err := function(stopID, stopCode, tripID, ctx, t)
  166. if err != nil {
  167. log.Printf("while getting alerts: %v\n", err)
  168. return []SpecificAlert{}
  169. }
  170. return selectSpecificAlerts(alerts, languages)
  171. }
  172. return []SpecificAlert{}
  173. }
  174. func getVehiclePositions(ctx Context, t *Traffic, lb, rt Position) []VehicleStatus {
  175. feedInfo, err := getFeedInfo(ctx.DataHome, ctx.FeedID, ctx.Version)
  176. if err != nil {
  177. log.Printf("while getting feedInfo: %v\n", err)
  178. feedInfo = FeedInfo{}
  179. }
  180. var function func(Context, Position, Position) ([]VehicleStatus, error)
  181. if feedInfo.Name != "" {
  182. if _, ok := feedInfo.RealtimeFeeds[VEHICLE_POSITIONS]; ok {
  183. function = getGtfsRealtimeVehicles
  184. } else if isLuaVehiclesScript(ctx) {
  185. function = getLuaRealtimeVehicles
  186. }
  187. }
  188. if function != nil {
  189. statuses, err := function(ctx, lb, rt)
  190. if err != nil {
  191. log.Printf("while getting vehicle positions: %v\n", err)
  192. return []VehicleStatus{}
  193. }
  194. file, err := openTrips(ctx)
  195. if err != nil {
  196. log.Printf("while opening trips file: %v", err)
  197. return []VehicleStatus{}
  198. }
  199. defer file.Close()
  200. statusesWithLine := make([]VehicleStatus, len(statuses))
  201. tripIDs := []string{}
  202. for i, status := range statuses {
  203. if status.LineID != "" && status.Headsign != "" {
  204. statusesWithLine[i] = status
  205. } else {
  206. tripIDs = append(tripIDs, status.TripID)
  207. }
  208. }
  209. trips, err := GetTrips(tripIDs, ctx, t)
  210. if err != nil {
  211. log.Printf("while getting trips file: %v", err)
  212. return []VehicleStatus{}
  213. }
  214. for i, status := range statuses {
  215. if status.LineID == "" || status.Headsign == "" {
  216. trip := trips[status.TripID]
  217. status.LineID = trip.LineID
  218. status.Headsign = trip.Headsign
  219. statusesWithLine[i] = status
  220. }
  221. }
  222. return statusesWithLine
  223. }
  224. return []VehicleStatus{}
  225. }