Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Hive.Client.Console/3.2/HiveClientConsole.cs @ 2023

Last change on this file since 2023 was 2023, checked in by whackl, 15 years ago

implemented service methods to get and set an calender #468

File size: 22.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Diagnostics;
25using System.Drawing;
26using System.Linq;
27using System.Net;
28using System.ServiceModel;
29using System.Windows.Forms;
30using Calendar;
31using HeuristicLab.Hive.Client.Console.ClientService;
32using ZedGraph;
33using HeuristicLab.Hive.Contracts;
34using System.Xml.Serialization;
35using System.IO;
36
37namespace HeuristicLab.Hive.Client.Console
38{
39
40  #region Delegates
41
42  public delegate void AppendTextDelegate(String message);
43  public delegate void RemoveTextDelegate(int newLength, int maxChars);
44  public delegate void OnDialogClosedDelegate(RecurrentEvent e);
45
46  #endregion
47
48  public partial class HiveClientConsole : Form
49  {
50
51    #region Declarations
52
53    private const string ENDPOINTADRESS = "net.tcp://127.0.0.1:8000/ClientConsole/ClientConsoleCommunicator";
54    //private const string EVENTLOGNAME = "Hive Client";
55
56    //private EventLog HiveClientEventLog;
57    private LogFileReader logFileReader;
58    private ClientConsoleCommunicatorClient cccc;
59    private System.Windows.Forms.Timer refreshTimer;
60    //private ListViewColumnSorterDate lvwColumnSorter;
61
62    [XmlArray("Appointments")]
63    [XmlArrayItem("Appointment",typeof(Appointment))]
64    public List<Appointment> onlineTimes = new List<Appointment>();
65
66    public OnDialogClosedDelegate dialogClosedDelegate;
67
68    #endregion
69
70    #region Constructor
71
72    public HiveClientConsole()
73    {
74      InitializeComponent();
75      InitTimer();
76      ConnectToClient();
77      RefreshGui();
78      InitCalender();
79      InitLogFileReader();
80    }
81
82    private void InitTestCalenderEntries()
83    {
84        DateTime date = DateTime.Now;
85        while (date.Year == 2009)
86        {
87           
88            onlineTimes.Add(CreateAppointment(date.AddHours(1), date.AddHours(3), false));
89            date = date.AddDays(1);
90        }
91    }
92
93    #endregion
94
95    #region Methods
96
97    private void InitLogFileReader() {
98      logFileReader = new LogFileReader(Environment.CurrentDirectory + @"/Hive.log");
99      logFileReader.MoreData += new LogFileReader.MoreDataHandler(logFileReader_MoreData);
100      logFileReader.Start();
101    }
102
103    private void logFileReader_MoreData(object sender, string newData) {
104      int maxChars = txtLog.MaxLength;
105      if (newData.Length > maxChars) {
106        newData = newData.Remove(0, newData.Length - maxChars);
107      }
108      int newLength = this.txtLog.Text.Length + newData.Length;
109      if (newLength > maxChars) {
110          RemoveText(newLength, maxChars);
111      }
112      AppendText(newData);
113    }
114
115    private void RemoveText(int newLength, int maxChars)
116    {
117        if (this.txtLog.InvokeRequired)
118        {
119            this.txtLog.Invoke(new
120              RemoveTextDelegate(RemoveText), new object[] { newLength, maxChars });
121        }
122        else
123        {
124            this.txtLog.Text = this.txtLog.Text.Remove(0, newLength - (int)maxChars);
125        }
126    }
127
128    private void InitTimer()
129    {
130      refreshTimer = new System.Windows.Forms.Timer();
131      refreshTimer.Interval = 1000;
132      refreshTimer.Tick += new EventHandler(refreshTimer_Tick);
133      refreshTimer.Start();
134    }
135
136    private void RefreshGui()
137    {
138      try
139      {
140        cccc.GetStatusInfosAsync();
141      }
142      catch (Exception ex)
143      {
144        refreshTimer.Stop();
145        DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running!", "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
146        if (res == DialogResult.OK)
147          this.Close();
148      }
149    }
150
151    private void ConnectToClient()
152    {
153      try
154      {
155        //changed by MB, 16.04.09
156        //cccc = new ClientConsoleCommunicatorClient(new NetTcpBinding(), new EndpointAddress(ENDPOINTADRESS));
157        cccc = new ClientConsoleCommunicatorClient(WcfSettings.GetBinding(), new EndpointAddress(ENDPOINTADRESS));
158        cccc.GetStatusInfosCompleted += new EventHandler<GetStatusInfosCompletedEventArgs>(cccc_GetStatusInfosCompleted);
159        cccc.GetCurrentConnectionCompleted += new EventHandler<GetCurrentConnectionCompletedEventArgs>(cccc_GetCurrentConnectionCompleted);
160        cccc.GetUptimeCalendarCompleted += new EventHandler<GetUptimeCalendarCompletedEventArgs>(cccc_GetUptimeCalendarCompleted);
161        cccc.SetUptimeCalendarCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(cccc_SetUptimeCalendarCompleted);
162      }
163      catch (Exception)
164      {
165        refreshTimer.Stop();
166        DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running!", "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
167        if (res == DialogResult.OK)
168          this.Close();
169      }
170    }
171
172    void cccc_SetUptimeCalendarCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
173    {
174        if (e.Error == null)
175        {
176            MessageBox.Show("Calendar successfully saved!", "Calender", MessageBoxButtons.OK, MessageBoxIcon.Information);
177        }
178        else
179        {
180            MessageBox.Show("Error saving calender \n" + e.Error.ToString(), "Calender", MessageBoxButtons.OK, MessageBoxIcon.Error);
181        }
182    }
183
184    void cccc_GetUptimeCalendarCompleted(object sender, GetUptimeCalendarCompletedEventArgs e)
185    {
186        if (e.Error == null)
187        {
188            if (e.Result != null)
189            {
190                onlineTimes = e.Result.ToList<Appointment>();
191                onlineTimes.ForEach(a => a.BorderColor = Color.Red);
192            }
193            else
194            {
195                onlineTimes = new List<Appointment>();
196            }
197        }
198        //InitTestCalenderEntries();
199    }
200
201    private void UpdateGraph(JobStatus[] jobs)
202    {
203      ZedGraphControl zgc = new ZedGraphControl();
204      GraphPane myPane = zgc.GraphPane;
205      myPane.GraphObjList.Clear();
206
207      myPane.Title.IsVisible = false;  // no title
208      myPane.Border.IsVisible = false; // no border
209      myPane.Chart.Border.IsVisible = false; // no border around the chart
210      myPane.XAxis.IsVisible = false;  // no x-axis
211      myPane.YAxis.IsVisible = false;  // no y-axis
212      myPane.Legend.IsVisible = false; // no legend
213
214      myPane.Fill.Color = this.BackColor;
215
216      myPane.Chart.Fill.Type = FillType.None;
217      myPane.Fill.Type = FillType.Solid;
218
219      double allProgress = 0;
220      double done = 0;
221
222      if (jobs.Length == 0)
223      {
224        myPane.AddPieSlice(100, Color.Green, 0.1, "");
225      }
226      else
227      {
228        for (int i = 0; i < jobs.Length; i++)
229        {
230          allProgress += jobs[i].Progress;
231        }
232
233        done = allProgress / jobs.Length;
234
235        myPane.AddPieSlice(done, Color.Green, 0, "");
236        myPane.AddPieSlice(1 - done, Color.Red, 0, "");
237      }
238      //Hides the slice labels
239      PieItem.Default.LabelType = PieLabelType.None;
240
241      myPane.AxisChange();
242
243      pbGraph.Image = zgc.GetImage();
244    }
245
246    private void InitCalender()
247    {
248      dvOnline.StartDate = DateTime.Now;
249      dvOnline.OnNewAppointment += new EventHandler<NewAppointmentEventArgs>(DvOnline_OnNewAppointment);
250      dvOnline.OnResolveAppointments += new EventHandler<ResolveAppointmentsEventArgs>(DvOnline_OnResolveAppointments);
251
252        //get calender from client
253      cccc.GetUptimeCalendarAsync();
254    }
255
256    private Appointment CreateAppointment(DateTime startDate, DateTime endDate, bool allDay)
257    {
258      Appointment App = new Appointment();
259      App.StartDate = startDate;
260      App.EndDate = endDate;
261      App.AllDayEvent = allDay;
262      App.BorderColor = Color.Red;
263      App.Locked = true;
264      App.Subject = "Online";
265      App.Recurring = false;
266      return App;
267    }
268
269    private Appointment CreateAppointment(DateTime startDate, DateTime endDate, bool allDay, bool recurring, Guid recurringId)
270    {
271      Appointment App = new Appointment();
272      App.StartDate = startDate;
273      App.EndDate = endDate;
274      App.AllDayEvent = allDay;
275      App.BorderColor = Color.Red;
276      App.Locked = true;
277      App.Subject = "Online";
278      App.Recurring = recurring;
279      App.RecurringId = recurringId;
280      return App;
281    }
282
283    private void DeleteAppointment()
284    {
285      onlineTimes.Remove(dvOnline.SelectedAppointment);
286    }
287
288    private void DeleteRecurringAppointment(Guid recurringId)
289    {
290      onlineTimes.RemoveAll(a => a.RecurringId.ToString() == dvOnline.SelectedAppointment.RecurringId.ToString());
291    }
292
293    #endregion
294
295    #region Events
296
297    private void refreshTimer_Tick(object sender, EventArgs e)
298    {
299      RefreshGui();
300    }
301
302    private void cccc_GetCurrentConnectionCompleted(object sender, GetCurrentConnectionCompletedEventArgs e)
303    {
304      if (e.Error == null)
305      {
306        ConnectionContainer curConnection = e.Result;
307        tbIPAdress.Text = curConnection.IPAdress;
308        tbPort.Text = curConnection.Port.ToString();
309      }
310      else
311      {
312        refreshTimer.Stop();
313        DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running! - " + e.Error.Message, "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
314        if (res == DialogResult.OK)
315          this.Close();
316      }
317    }
318
319    private void cccc_GetStatusInfosCompleted(object sender, GetStatusInfosCompletedEventArgs e)
320    {
321
322      if (e.Error == null)
323      {
324        StatusCommons sc = e.Result;
325
326        lbGuid.Text = sc.ClientGuid.ToString();
327        lbConnectionStatus.Text = sc.Status.ToString();
328        lbJobdone.Text = sc.JobsDone.ToString();
329        lbJobsAborted.Text = sc.JobsAborted.ToString();
330        lbJobsFetched.Text = sc.JobsFetched.ToString();
331
332        this.Text = "Client Console (" + sc.Status.ToString() + ")";
333
334        ListViewItem curJobStatusItem;
335
336        if (sc.Jobs != null)
337        {
338          lvJobDetail.Items.Clear();
339          double progress;
340          foreach (JobStatus curJob in sc.Jobs)
341          {
342            curJobStatusItem = new ListViewItem(curJob.JobId.ToString());
343            curJobStatusItem.SubItems.Add(curJob.Since.ToString());
344            progress = curJob.Progress * 100;
345            curJobStatusItem.SubItems.Add(progress.ToString());
346            lvJobDetail.Items.Add(curJobStatusItem);
347          }
348          lvJobDetail.Sort();
349        }
350
351        UpdateGraph(sc.Jobs);
352
353        if (sc.Status == NetworkEnumWcfConnState.Connected || sc.Status == NetworkEnumWcfConnState.Loggedin)
354        {
355          btConnect.Enabled = false;
356          btnDisconnect.Enabled = true;
357          lbCs.Text = sc.ConnectedSince.ToString();
358          cccc.GetCurrentConnectionAsync();
359        }
360        else if (sc.Status == NetworkEnumWcfConnState.Disconnected)
361        {
362          btConnect.Enabled = true;
363          btnDisconnect.Enabled = false;
364          lbCs.Text = String.Empty;
365        }
366        else if (sc.Status == NetworkEnumWcfConnState.Failed)
367        {
368          btConnect.Enabled = true;
369          btnDisconnect.Enabled = false;
370          lbCs.Text = String.Empty;
371        }
372
373        cccc.GetCurrentConnection();
374      }
375      else
376      {
377        refreshTimer.Stop();
378        DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running! - " + e.Error.Message, "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
379        if (res == DialogResult.OK)
380          this.Close();
381      }
382    }
383
384    private void HiveClientConsole_Load(object sender, EventArgs e)
385    {
386      //nothing to do
387    }
388
389    private void AppendText(string message) {
390      if (this.txtLog.InvokeRequired) {
391        this.txtLog.Invoke(new
392          AppendTextDelegate(AppendText), new object[] { message });
393      } else {
394        this.txtLog.AppendText(message);
395      }
396    }
397
398    private void btConnect_Click(object sender, EventArgs e)
399    {
400      IPAddress ipAdress;
401      int port;
402      ConnectionContainer cc = new ConnectionContainer();
403      if (IPAddress.TryParse(tbIPAdress.Text, out ipAdress) && int.TryParse(tbPort.Text, out port))
404      {
405        cc.IPAdress = tbIPAdress.Text;
406        cc.Port = port;
407        cccc.SetConnectionAsync(cc);
408      }
409      else
410      {
411        MessageBox.Show("IP Adress and/or Port Error", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
412      }
413    }
414
415    private void btnDisconnect_Click(object sender, EventArgs e)
416    {
417      cccc.DisconnectAsync();
418    }
419
420    private void btn_clientShutdown_Click(object sender, EventArgs e)
421    {
422      DialogResult res = MessageBox.Show("Do you really want to shutdown the Hive Client?", "Hive Client Console", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
423      if (res == DialogResult.Yes)
424      {
425          logFileReader.Stop();
426        cccc.ShutdownClient();
427        this.Close();
428      }
429    }
430
431    private void btbDelete_Click(object sender, EventArgs e)
432    {
433      Appointment selectedAppointment = dvOnline.SelectedAppointment;
434      if (dvOnline.SelectedAppointment != null)
435      {
436        if (!selectedAppointment.Recurring)
437          DeleteAppointment();
438        else
439        {
440          DialogResult res = MessageBox.Show("Delete all events in this series?", "Delete recurrences", MessageBoxButtons.YesNo);
441          if (res != DialogResult.Yes)
442            DeleteAppointment();
443          else
444            DeleteRecurringAppointment(selectedAppointment.RecurringId);
445        }
446      }
447      dvOnline.Invalidate();
448    }
449
450    private void chbade_CheckedChanged(object sender, EventArgs e)
451    {
452      txttimeFrom.Visible = !chbade.Checked;
453      txttimeTo.Visible = !chbade.Checked;
454    }
455
456    private void dvOnline_OnSelectionChanged(object sender, EventArgs e)
457    {
458      //btCreate.Enabled = true;
459      if (dvOnline.Selection == SelectionType.DateRange)
460      {
461        dtpFrom.Text = dvOnline.SelectionStart.ToShortDateString();
462        dtpTo.Text = dvOnline.SelectionEnd.Date.ToShortDateString();
463        txttimeFrom.Text = dvOnline.SelectionStart.ToShortTimeString();
464        txttimeTo.Text = dvOnline.SelectionEnd.ToShortTimeString();
465
466        btCreate.Text = "Save";
467      }
468
469      if (dvOnline.Selection == SelectionType.Appointment)
470      {
471
472        dtpFrom.Text = dvOnline.SelectedAppointment.StartDate.ToShortDateString();
473        dtpTo.Text = dvOnline.SelectedAppointment.EndDate.ToShortDateString();
474        txttimeFrom.Text = dvOnline.SelectedAppointment.StartDate.ToShortTimeString();
475        txttimeTo.Text = dvOnline.SelectedAppointment.EndDate.ToShortTimeString();
476
477        if (dvOnline.SelectedAppointment.Recurring)
478          //btCreate.Enabled = false;
479        //also change the caption of the save button
480        btCreate.Text = "Save changes";
481      }
482
483      if (dvOnline.Selection == SelectionType.None)
484      {
485        //also change the caption of the save button
486        btCreate.Text = "Save";
487      }
488
489    }
490
491    private void Connection_KeyPress(object sender, KeyPressEventArgs e)
492    {
493      if (e.KeyChar == (char)Keys.Return)
494        btConnect_Click(null, null);
495    }
496
497    private void mcOnline_DateChanged(object sender, DateRangeEventArgs e)
498    {
499      dvOnline.StartDate = mcOnline.SelectionStart;
500    }
501
502    private bool CreateAppointment()
503    {
504      DateTime from, to;
505
506      if (!string.IsNullOrEmpty(dtpFrom.Text) && !string.IsNullOrEmpty(dtpTo.Text))
507      {
508        if (chbade.Checked)
509        {
510          //whole day appointment, only dates are visible
511          if (DateTime.TryParse(dtpFrom.Text + " " + txttimeFrom.Text, out from) && DateTime.TryParse(dtpTo.Text + " " + txttimeTo.Text, out to) && from < to)
512            onlineTimes.Add(CreateAppointment(from, to.AddDays(1), true));
513          else
514            MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
515        }
516        else if (!string.IsNullOrEmpty(txttimeFrom.Text) && !string.IsNullOrEmpty(txttimeTo.Text))
517        {
518          //Timeframe appointment
519          if (DateTime.TryParse(dtpFrom.Text + " " + txttimeFrom.Text, out from) && DateTime.TryParse(dtpTo.Text + " " + txttimeTo.Text, out to) && from < to)
520          {
521            if (from.Date == to.Date)
522              onlineTimes.Add(CreateAppointment(from, to, false));
523            else
524            {
525              //more than 1 day selected
526              while (from.Date != to.Date)
527              {
528                onlineTimes.Add(CreateAppointment(from, new DateTime(from.Year, from.Month, from.Day, to.Hour, to.Minute, 0, 0), false));
529                from = from.AddDays(1);
530              }
531              onlineTimes.Add(CreateAppointment(from, new DateTime(from.Year, from.Month, from.Day, to.Hour, to.Minute, 0, 0), false));
532            }
533          }
534          else
535            MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
536        }
537        dvOnline.Invalidate();
538        return true;
539      }
540      else
541      {
542        MessageBox.Show("Error in create appointment, please fill out all textboxes!", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
543        return false;
544      }
545    }
546
547    private void btCreate_Click(object sender, EventArgs e)
548    {
549      if (dvOnline.Selection != SelectionType.Appointment)
550      {
551        CreateAppointment();
552      }
553      else
554      {
555        //now we want to change an existing appointment
556        if (!dvOnline.SelectedAppointment.Recurring)
557        {
558          if (CreateAppointment())
559            DeleteAppointment();
560        }
561        else
562        {
563          //change recurring appointment
564          //check, if only selected appointment has to change or whole recurrence
565          DialogResult res = MessageBox.Show("Change all events in this series?", "Change recurrences", MessageBoxButtons.YesNo);
566          if (res != DialogResult.Yes)
567          {
568            if (CreateAppointment())
569              DeleteAppointment();
570          }
571          else
572            ChangeRecurrenceAppointment(dvOnline.SelectedAppointment.RecurringId);
573        }
574      }
575    }
576
577    private void ChangeRecurrenceAppointment(Guid recurringId)
578    {
579      int hourfrom = int.Parse(txttimeFrom.Text.Substring(0, txttimeFrom.Text.IndexOf(':')));
580      int hourTo = int.Parse(txttimeTo.Text.Substring(0, txttimeTo.Text.IndexOf(':')));
581      List<Appointment> recurringAppointments = onlineTimes.Where(appointment => appointment.RecurringId == recurringId).ToList();     
582      recurringAppointments.ForEach(appointment => appointment.StartDate = new DateTime(appointment.StartDate.Year, appointment.StartDate.Month, appointment.StartDate.Day, hourfrom, 0, 0 ));
583      recurringAppointments.ForEach(appointment => appointment.EndDate = new DateTime(appointment.EndDate.Year, appointment.EndDate.Month, appointment.EndDate.Day, hourTo, 0, 0));
584
585      DeleteRecurringAppointment(recurringId);
586      onlineTimes.AddRange(recurringAppointments);
587    }
588
589    private void DvOnline_OnResolveAppointments(object sender, ResolveAppointmentsEventArgs e)
590    {
591      List<Appointment> Apps = new List<Appointment>();
592
593      foreach (Appointment m_App in onlineTimes)
594        if ((m_App.StartDate >= e.StartDate) &&
595            (m_App.StartDate <= e.EndDate))
596          Apps.Add(m_App);
597      e.Appointments = Apps;
598    }
599
600    private void DvOnline_OnNewAppointment(object sender, NewAppointmentEventArgs e)
601    {
602      Appointment Appointment = new Appointment();
603
604      Appointment.StartDate = e.StartDate;
605      Appointment.EndDate = e.EndDate;
606
607      onlineTimes.Add(Appointment);
608    }
609
610    private void btnRecurrence_Click(object sender, EventArgs e)
611    {
612      Recurrence recurrence = new Recurrence();
613      recurrence.dialogClosedDelegate = new OnDialogClosedDelegate(this.DialogClosed);
614      recurrence.Show();
615    }
616
617    public void DialogClosed(RecurrentEvent e)
618    {
619      CreateDailyRecurrenceAppointments(e.DateFrom, e.DateTo, e.AllDay, e.IncWeeks, e.WeekDays);
620    }
621
622    private void CreateDailyRecurrenceAppointments(DateTime dateFrom, DateTime dateTo, bool allDay, int incWeek, HashSet<DayOfWeek> daysOfWeek) {
623      DateTime incDate = dateFrom;
624      Guid guid = Guid.NewGuid();
625
626      while (incDate.Date <= dateTo.Date) {
627        if (daysOfWeek.Contains(incDate.Date.DayOfWeek))
628          onlineTimes.Add(CreateAppointment(incDate, new DateTime(incDate.Year, incDate.Month, incDate.Day, dateTo.Hour, dateTo.Minute, 0), allDay, true, guid));
629        incDate = incDate.AddDays(1);
630      }
631
632      dvOnline.Invalidate();
633    }
634
635
636   //private void SerializeCalender() {
637     
638   //    XmlSerializer s = new XmlSerializer(typeof(List<Appointment>));
639   //    TextWriter w = new StreamWriter(@"c:\temp\apptest.xml");
640   //    s.Serialize(w, onlineTimes);
641   //    w.Close();
642   //}
643
644   //private void DeSerializeCalender()
645   //{
646   //    XmlSerializer s = new XmlSerializer(typeof(List<Appointment>));
647   //    TextReader r = new StreamReader(@"c:\temp\apptest.xml");
648   //    onlineTimes = (List<Appointment>)s.Deserialize(r);
649   //    onlineTimes.ForEach(a => a.BorderColor = Color.Red);
650   //    r.Close();
651
652   //}
653
654   private void btnSaveCal_Click(object sender, EventArgs e)
655   {
656       cccc.SetUptimeCalendarAsync(onlineTimes.ToArray());
657   }
658
659    #endregion
660
661  }
662}
Note: See TracBrowser for help on using the repository browser.