-
Notifications
You must be signed in to change notification settings - Fork 1
/
Program.cs
360 lines (299 loc) · 10.9 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
using System;
using System.IO;
using System.Linq;
using System.Web;
using System.Reflection;
using Microsoft.Extensions.Configuration;
using System.ServiceModel.Channels;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Terminal.Gui;
using ConfigurationManager = Terminal.Gui.ConfigurationManager;
using LDB;
namespace DepartureBoard
{
static class Program
{
static readonly LDBServiceSoapClient _client = new LDBServiceSoapClient(LDBServiceSoapClient.EndpointConfiguration.LDBServiceSoap12);
static readonly AccessToken _token = new AccessToken();
static Dictionary<string, string> _stationList = new Dictionary<string, string>();
static Window _mainWindow;
static MenuBar _menuBar;
static List<string> _rsids;
static ListView _displayBoard;
static ListView _displayDetails;
static ListView _displayMessages;
static string _fromStationCode;
static string _toStationCode;
const string _allDestinations = "all destinations";
static MenuItem _switchMenu;
static MenuItem _allDestinationMenu;
static void Main(string[] args)
{
Application.Init();
Application.AddTimeout(TimeSpan.FromMinutes(5), Refresh);
#if !TEST
// token from command line?
var token = args.Length == 1 || args.Length == 3 ? args [^1] : null;
// appsetting
var config = new ConfigurationBuilder().AddJsonFile("appSettings.json").Build();
_token.TokenValue = token ?? config["token"];
if (!Guid.TryParse(_token.TokenValue, out Guid dummy))
{
MessageBox.ErrorQuery(80, 7, "Error", $"Invalid token:- {_token.TokenValue}", 0, "Quit");
Application.Shutdown();
return;
}
#endif
// station list
var file = File.ReadAllLines("station_codes.csv");
_stationList = file.ToDictionary(k => k.Split(',')[0], v => v.Split(',')[1]);
// Web proxy https://github.com/dotnet/wcf/issues/3311
if (_client.Endpoint.Binding is CustomBinding custom)
{
var binding = custom.Elements.Find<HttpsTransportBindingElement>();
binding.UseDefaultWebProxy = true;
}
// menu bar
var themesMenu = new MenuItem[ConfigurationManager.Themes.Count];
int i = 0;
foreach (var theme in ConfigurationManager.Themes.Keys)
themesMenu[i++] = new MenuItem(theme, "", () => SetColorScheme(theme));
_menuBar = new MenuBar()
{
Menus = new MenuBarItem[]
{
new MenuBarItem("_File", new MenuItem[]
{
new MenuItem("_New", "", New),
new MenuItem("_Quit", "", () => { if (Quit()) Application.Top.Running = false; })
}),
new MenuBarItem("_Options", new MenuItem[]
{
new MenuItem("_Refresh", "", GetBoard),
new MenuBarItem("_Switch", new MenuItem[]
{
_switchMenu = new MenuItem("#swap#", "", Switch),
_allDestinationMenu = new MenuItem("#alldest#", "", AllDestinations)
}),
new MenuBarItem("_Theme", themesMenu)
}),
new MenuBarItem("_Help", new MenuItem[]
{
new MenuItem("_About", "", About)
})
}};
// main window
_mainWindow = new Window() { Title = "Departures", X = 0, Y = Pos.Bottom(_menuBar), Width = Dim.Fill(), Height = Dim.Fill(), BorderStyle = LineStyle.Single };
_mainWindow.KeyDown += (object sender, Key e) =>
{
if (e.KeyCode == Key.F5)
GetBoard();
};
var top = new Toplevel();
top.Add(_menuBar, _mainWindow);
// Main board
_displayBoard = new ListView() { X = 0, Y = 0, Width = Dim.Fill(), Height = Dim.Percent(50) };
_displayBoard.SelectedItemChanged += GetDetialsBoard;
_mainWindow.Add(_displayBoard);
// Details board
_displayDetails = new ListView() { X = 0, Y = Pos.Bottom(_displayBoard), Width = Dim.Fill(), Height = Dim.Percent(25) };
_mainWindow.Add(_displayDetails);
// messages
var viewMessage = new FrameView() { Title = "Messages", X = 0, Y = Pos.Bottom(_displayDetails)+1, Width = Dim.Fill(), Height = Dim.Fill(), BorderStyle = LineStyle.Single };
_displayMessages = new ListView() { X = 0, Y = 0, Width = Dim.Fill(), Height = Dim.Fill() };
viewMessage.Add(_displayMessages);
viewMessage.TabStop = TabBehavior.NoStop; // needs to happen after control added?!?
_mainWindow.Add(viewMessage);
// Run
if (args.Length >= 2)
{
_fromStationCode = args[0];
_toStationCode = args[1] == "ALL" ? null : args[1];
if(!_stationList.ContainsValue(_fromStationCode))
MessageBox.ErrorQuery(80, 7, "Error", $"Invalid 'from' station code:- {_fromStationCode}", 0, "Continue");
else if(!_stationList.ContainsValue(_toStationCode) && _toStationCode != null)
MessageBox.ErrorQuery(80, 7, "Error", $"Invalid 'to' station code:- {_toStationCode}", 0, "Continue");
else
Application.Invoke(GetBoard);
}
else
Application.Invoke(New);
Application.Run(top);
Application.Shutdown();
}
static void New()
{
if ((_fromStationCode = SelectStation("From station")) == string.Empty)
return;
_displayBoard.SetFocus();
if ((_toStationCode = SelectStation("To station")) == string.Empty)
return;
GetBoard();
}
static string SelectStation(string title)
{
const string all = $"<{_allDestinations}>";
var list = title.StartsWith("To ") ? _stationList.Keys.Prepend(all).ToList() : _stationList.Keys.ToList();
var stationSearch = new ComboBox() { Width = Dim.Fill(), Height = Dim.Fill() };
stationSearch.SetSource(new ObservableCollection<string>(list));
string selected = null;
stationSearch.OpenSelectedItem += (object sender, ListViewItemEventArgs e) =>
{
selected = e.Value.ToString();
Application.RequestStop();
};
var dialog = new Dialog() { Title = title, Width = Dim.Percent(40), Height = Dim.Percent(50) };
dialog.Add(stationSearch);
Application.Run(dialog);
if (selected == all)
return null;
if (selected == null) // dialog cancelled
return string.Empty;
return _stationList[selected];
}
static void Switch()
{
if (_toStationCode == null) // cannot switch to from "All Destiations"
return;
(_toStationCode, _fromStationCode) = (_fromStationCode, _toStationCode);
GetBoard();
}
static void AllDestinations()
{
_toStationCode = null;
GetBoard();
}
static void About()
{
var ok = new Button() { Text = "Ok", IsDefault = true };
ok.Accept += (object sender, HandledEventArgs e) => Application.RequestStop();
var about = new Dialog() { Title = "About", Width = 36, Height = 8, BorderStyle = LineStyle.Single };
about.AddButton(ok);
about.Add(
new Label() { Text = $"Live DepartureBoard {Assembly.GetEntryAssembly().GetName().Version}", Width = Dim.Fill(), Y = 1, TextAlignment = Alignment.Center },
new Label() { Text = $"{Environment.OSVersion.VersionString}", Width = Dim.Fill(), Y = 2, TextAlignment = Alignment.Center },
new Label() { Text = $"DotNet {Environment.Version}", Width = Dim.Fill(), Y = 3, TextAlignment = Alignment.Center }
);
Application.Run(about);
}
static bool Quit()
{
return MessageBox.Query(50, 7, "Quit?", "Are you sure you want to quit?", 0, "Yes", "No") == 0;
}
static bool Refresh()
{
GetBoard();
return true; // keep ticking
}
static void SetColorScheme(string name)
{
ConfigurationManager.Themes.Theme = name;
ConfigurationManager.Apply();
_mainWindow.SetNeedsDisplay();
}
static void GetBoard()
{
_displayBoard.Source = null;
_displayDetails.Source = null;
_displayMessages.Source = null;
_displayBoard.SetFocus();
_rsids?.Clear();
StationBoard2 board;
#if TEST
board = new StationBoard2
{
locationName = _stationList.First(x => x.Value == _fromStationCode).Key,
filterType = FilterType.to,
filterLocationName = _stationList.FirstOrDefault(x => x.Value == _toStationCode).Key,
trainServices = new ServiceItem2[10],
nrccMessages = new NRCCMessage[] { new NRCCMessage { Value = "Blackheath toilets out of order" } }
};
var time = DateTime.Now;
for (int i = 0; i < board.trainServices.Length; i++)
{
board.trainServices[i] = new ServiceItem2
{
std = time.ToString("hh:mm"),
destination = new ServiceLocation[] { new ServiceLocation { locationName = board.filterLocationName } },
platform = "1",
etd = "On time",
@operator = "Southeatern",
serviceID = "1234"
};
time = time.AddMinutes(4);
}
#else
try
{
board = _client.GetDepartureBoard(_token, 10, _fromStationCode, _toStationCode, FilterType.to, 0, 120);
}
catch (Exception e)
{
MessageBox.ErrorQuery(78, 10, "Error", e.Message, 0, "Continue");
return;
}
#endif
_mainWindow.Title = $"{board.locationName} {board.filterType} {board.filterLocationName ?? _allDestinations}";
_switchMenu.Title = $"{board.filterLocationName ?? _allDestinations} -> {board.locationName}";
_allDestinationMenu.Title = $"{board.locationName} -> {_allDestinations}";
bool switchMenuEnabled() { return board.filterLocationName != null; }
_switchMenu.CanExecute = switchMenuEnabled;
_allDestinationMenu.CanExecute = switchMenuEnabled;
if (board.trainServices == null)
return;
_displayBoard.SetSource(new ObservableCollection<string>(board.trainServices.Select(x => $"{x.std} {x.destination[0].locationName,-25} {x.platform,-4} {x.etd,-10} {x.@operator}").ToList()));
_displayBoard.SelectedItem = 0;
_rsids = board.trainServices.Select(x => x.serviceID).ToList();
if (board.nrccMessages == null)
return;
_displayMessages.SetSource(new ObservableCollection<string>(board.nrccMessages.Select(x => HttpUtility.HtmlDecode( x.Value)).ToList()));
}
static void GetDetialsBoard(object sender, ListViewItemEventArgs e)
{
if (_rsids == null || _rsids.Count == 0) // fired before GetBoard called
return;
var serviceId = _rsids[_displayBoard.SelectedItem];
if (serviceId == null)
return;
ServiceDetails details;
#if TEST
details = new ServiceDetails
{
subsequentCallingPoints = MakeCallingPoints(
"Lewisham", "Lewisham2", "Lewisham3","Lewisham4","Lewisham5", // padding for test
"St Johns",
"New Cross",
"London Bridge",
"London Cannon Street")
};
#else
try
{
details = _client.GetServiceDetails(_token, serviceId);
}
catch (Exception ex)
{
_displayMessages.SetSource(new ObservableCollection<string>(new List<string> { ex.Message }));
return;
}
#endif
_displayDetails.SetSource(new ObservableCollection<string>(details.subsequentCallingPoints[0].callingPoint.Select(x => $"{x.st} {x.locationName,-25} {x.et,-10}").ToList()));
_displayDetails.SelectedItem = 0;
}
#if TEST
static ArrayOfCallingPoints1[] MakeCallingPoints(params string[] callingPoints)
{
var data = new ArrayOfCallingPoints1[]
{
new ArrayOfCallingPoints1 { callingPoint = new CallingPoint1[callingPoints.Length] }
};
var time = DateTime.Now;
for (int i = 0; i < callingPoints.Length; i++, time = time.AddMinutes(4))
data[0].callingPoint[i] = new CallingPoint1 { st = time.ToString("hh:mm"), locationName = callingPoints[i], et = "On time" };
return data;
}
#endif
}
}