private void btnExtract_Click(object sender, EventArgs e) { this.DispatchToBackground(() => { var A = new List<AnalyzedActivity>(); using (var dbConn = DatabaseManager.DbConn()) { A.AddRange(dbConn.ExecuteBpl(new TelemetryVehicleActivityGetAll()).Select(a => new AnalyzedActivity { Activity = a })); _report("Activities: {0}", A.Count); for (var i = 0; i < A.Count; i++) { var a = A[i]; a.StartMeters = dbConn.ExecuteBpl(new TelemetryVehicleActivityMetersGetByActivityIdType { ActivityId = a.Activity.ActivityId, RecordType = VehicleMetersRecordType.StartMeters }); a.FinishMeters = dbConn.ExecuteBpl(new TelemetryVehicleActivityMetersGetByActivityIdType { ActivityId = a.Activity.ActivityId, RecordType = VehicleMetersRecordType.FinishMeters }); } A.RemoveAll(a => a.Activity.ActivityType != VehicleMode.Driving || (a.FinishMeters.Odometer - a.StartMeters.Odometer) < (Distance)10); _report("Usable driving activities: {0}", A.Count); var AA = A.ToDictionary(a => a.Activity.ActivityId.ToString(false)); var T = dbConn.ExecuteBpl(new TelemetryGetAll()); _report("Records: {0}", T.Count); var TT = new Dictionary<string, VehicleTelemetry>(); for (var i = 0; i < T.Count; i++) { var dbt = T[i]; if (!dbt.Telemetry.IsManaged) continue; var a = AA.TryGet(dbt.Telemetry.ActivityId.ToString(false)); if (a == null) continue; var t = TT.TryGet(dbt.TelemetryId.ToString(false)); if (t == null) { t = dbt.Telemetry; a.Telemetry.Add(t); TT.Add(dbt.TelemetryId.ToString(false), t); } t.FutureDrivingSteps.Add(dbt.Step); } } A.RemoveAll(a => a.Telemetry.Count == 0); _report("Usable planned driving Activities: {0}", A.Count); // fix: all key in DB are 0 due to client bug in 2.9.7 (fixed in 2.9.8) foreach (var a in A) { // Log.Info("Activity: {0}, {1} records", a.Activity.ActivityId, a.Telemetry.Count); var lastDta = Distance.Undefined; var lastIndex = -1; var firstStepKey = -1; foreach (var t in a.Telemetry) { // Log.Info(" #{0} | {1} | Planned:{2} | {3} - O:{4}, S:{5}, i:{6}", t.RecordNo, t.TimeStamp, t.IsManaged, t.RecordType, t.Odometer, t.Battery.Soc, t.DrivingSegmentIndex); var stepCount = t.FutureDrivingSteps.Count; if (stepCount == 0) { lastDta = Distance.Undefined; lastIndex = -1; firstStepKey = -1; continue; } if (t.RecordType == TelemetryRecordType.DrivingPlanChanged || firstStepKey == -1 || lastIndex == -1) { lastDta = t.FutureDrivingSteps[0].Dta; lastIndex = t.DrivingSegmentIndex; firstStepKey = (int)Distance.ToMeters(t.FutureDrivingSteps[0].Dta); } // fix rule is: // - if index is 0, plan may change, so unless dDistance is 0 or very small - assume plan change // - if index dropped (from X to 0) - assume plan change // - some times index is dropped, but a bit later, so look at large dDistance change, as a fallback // (this is heuristics, but this is the best possible one, even though it's not precise - it's helpful sometimes) // - distance in meters used as a key, to fight cases with round-trip route where two steps have very similar distance (in meters - it will be different) // var dta = t.FutureDrivingSteps[0].Dta; var index = t.DrivingSegmentIndex; var ddta = (dta - lastDta).Abs(); var dindex = (index - lastIndex); if ((index == 0 && ddta < (Distance)0.5) || dindex < 0 || ddta > (Distance)2) { firstStepKey = (int)Distance.ToMeters(t.FutureDrivingSteps[0].Dta); } lastDta = dta; lastIndex = index; t.FutureDrivingSteps[0].Key = firstStepKey; for (var i = 1; i < t.FutureDrivingSteps.Count; i++) { t.FutureDrivingSteps[i].Key = (int)Distance.ToMeters(t.FutureDrivingSteps[i].Dta); } /* var str = ""; foreach (var s in t.FutureDrivingSteps) { str += "{0,7:#.000} | {1,6}, ".Substitute((double)s.Dta, s.Key); } Log.Info(" {0}", str); */ } } _report("Key fix applied"); _report("Saving extracted activities"); _activitiesList = new AnalyzedActivityList(); _activitiesList.Activities.AddRange(A); var formatter = BplXmlFormatter.Minimal; formatter.Format(_activitiesList); var xml = formatter.Output; File.WriteAllText("extracted.bpl", xml); _report("Extracting done"); }); }
private void btnAnalyze_Click(object sender, EventArgs e) { this.DispatchToBackground(() => { if (_activitiesList == null) { _report("Reading extracted activities"); var xml = File.ReadAllText("extracted.bpl"); var parser = new BplXmlParser(); parser.Parse(xml); _activitiesList = parser.Output as AnalyzedActivityList; } var A = _activitiesList.Activities; // create list of activities with telemetry segments longer than 10km of driving without structured plan changes _report("Start Analyzing. Total activities {0}", A.Count); var partSize = (Distance)20; var activities = new List<AnalyzedActivity>(); foreach (var a in A) { var telemetry = new List<VehicleTelemetry>(); Action tryAdd = () => { // plan change - measure part size, and if too short - clear and ignore var t0 = telemetry.FirstOrDefault(); var t1 = telemetry.LastOrDefault(); if (t0 == null || t1 == null) return; var d = t1.Odometer - t0.Odometer; if (d < partSize) { telemetry.Clear(); return; } var anew = new AnalyzedActivity { Activity = a.Activity.Clone() }; anew.Telemetry.AddRange(telemetry); activities.Add(anew); telemetry.Clear(); }; var tryadded = false; var partStarted = false; var lastKey = -1; for (var i = 0; i < a.Telemetry.Count; i++) { var t = a.Telemetry[i]; if (t.RecordType != TelemetryRecordType.DrivingPlanChanged && t.RecordType != TelemetryRecordType.DrivingRoadSegmentPassed) continue; // skip anything before first plan creation if (!partStarted) { if (t.RecordType == TelemetryRecordType.DrivingPlanChanged) partStarted = true; continue; } if (lastKey == -1) { var s = t.FutureDrivingSteps.FirstOrDefault(); if (s == null) continue; lastKey = s.Key; } // aggregate telemetry (with nearest destination only) as long as there's no plan change if (t.RecordType == TelemetryRecordType.DrivingRoadSegmentPassed) { var tnew = t.Clone(); if (tnew.OriginalDrivingSegmentEstimates.Eea.IsUndefined) continue; var snew = tnew.FutureDrivingSteps.FirstOrDefault().Clone(); if (snew == null) continue; if (snew.Key == lastKey) { tnew.FutureDrivingSteps.Clear(); tnew.FutureDrivingSteps.Add(snew); telemetry.Add(tnew); continue; } lastKey = snew.Key; } else if (t.RecordType == TelemetryRecordType.DrivingPlanChanged) { var s = t.FutureDrivingSteps.FirstOrDefault(); if (s == null) continue; lastKey = s.Key; } tryAdd(); tryadded = true; } if (!tryadded) tryAdd(); // try add remainings } _report("Done Analyzing. {0} - long usable planned driving activities: {1}", partSize, activities.Count); foreach (var a in activities) { var t0 = a.Telemetry.First(); var t1 = a.Telemetry.Last(); var slopeU = 0.0; var slenU = 0.0; var slopeD = 0.0; var slenD = 0.0; a.Telemetry.Apply(t => { var s = t.Segment; if (s.Slope > (Percent)0.04) { slopeU = (slopeU * slenU + (double)s.Slope * (double)s.Length) / (slenU + (double)s.Length); slenU += (double)s.Length; } else if (s.Slope < (Percent)(-0.04)) { slopeD = (slopeD * slenD + (double)s.Slope * (double)s.Length) / (slenD + (double)s.Length); slenD += (double)s.Length; } }); slopeU = (slopeU * 100.0).Round(1); slenU = slenU.Round(3); slopeD = (slopeD * 100.0).Round(1); slenD = slenD.Round(3); var o0 = t0.Odometer; var e0 = t0.Battery.Energy; var s0 = t0.Battery.Soc; // var a0 = t0.Gps.Location.Altitude; var o1 = t1.Odometer; var e1 = t1.Battery.Energy; var s1 = t1.Battery.Soc; // var a1 = t1.Gps.Location.Altitude; var dOdo = ((double)(o1 - o0)).Round(0); // var dAlt = ((double)(a1 - a0) * 1000).Round(0); var eest = ((double)t1.OriginalDrivingSegmentEstimates.Eea).Round(1); var eact = ((double)e1).Round(1); Debug.WriteLine("Distance: {0}, SlopeU: {1}, SlenU: {2}, SlopeD: {3}, SlenD: {4}, Eea: {5} ({6}%), E.actual: {7} ({8}%), Err: {9}%, ActivityId: {10}, Device: {11}, TimeStamp: {12}".Substitute( dOdo, slopeU, slenU, slopeD, slenD, eest, ((eest * 100) / 21.0).Round(0), eact, ((eact * 100) / 21.0).Round(0), ((eact * 100) / 21.0).Round(0) - ((eest * 100) / 21.0).Round(0), a.Activity.ActivityId, a.Activity.DeviceId, t1.TimeStamp )); } _report("Done Reporting"); }); }