aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/TechItems/HeaterItem.cs
blob: d419127c50ff7e0d9523a9b146b9f0707d24e79d (plain)
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Xml.Serialization;
using Tango.BL.Entities;
using Tango.Core.Commands;
using Tango.PMR.Diagnostics;
using Tango.SharedUI.Helpers;

namespace Tango.MachineStudio.Technician.TechItems
{
    /// <summary>
    /// Represents a heater controller.
    /// </summary>
    /// <seealso cref="Tango.MachineStudio.Technician.TechItems.TechItem" />
    [TechItem(10)]
    public class HeaterItem : TechItem
    {
        public event Action SetCommandClicked;

        private TechHeater _techHeater;
        /// <summary>
        /// Gets or sets the db tech monitor.
        /// </summary>
        [XmlIgnore]
        public TechHeater TechHeater
        {
            get { return _techHeater; }
            set { _techHeater = value; RaisePropertyChangedAuto(); TechName = _techHeater != null ? _techHeater.Description : null; ItemGuid = value != null ? value.Guid : null; }
        }

        /// <summary>
        /// Gets or sets the set command.
        /// </summary>
        [XmlIgnore]
        public RelayCommand SetCommand { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="HeaterItem"/> class.
        /// </summary>
        public HeaterItem() : base()
        {
            Name = "Heater Controller";
            Color = Colors.White;
            Description = "Heater Controller";
            Image = ResourceHelper.GetImageFromResources("Images/heater-controller.png");
            SetCommand = new RelayCommand(() => 
            {
                SetCommandClicked?.Invoke();
                IsEditing = false;
            }, (x) => HeaterState.CurrentValue != SetPoint);
            HeaterState = new HeaterState();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HeaterItem"/> class.
        /// </summary>
        /// <param name="TechHeater">The db tech monitor.</param>
        public HeaterItem(TechHeater techHeater) : this()
        {
            TechHeater = techHeater;
        }

        private HeaterState _heaterState;
        /// <summary>
        /// Gets or sets the state of the heater.
        /// </summary>
        public HeaterState HeaterState
        {
            get { return _heaterState; }
            set
            {
                _heaterState = value;
                RaisePropertyChangedAuto();

                if (!IsEditing)
                {
                    SetPoint = _heaterState.SetPoint;
                }

                InvokeUI(() =>
                {
                    SetCommand.RaiseCanExecuteChanged();
                });
            }
        }

        private double _setPoint;
        /// <summary>
        /// Gets or sets the set point.
        /// </summary>
        public double SetPoint
        {
            get { return _setPoint; }
            set { _setPoint = value; RaisePropertyChangedAuto(); }
        }

        private bool _isEditing;
        [XmlIgnore]
        public bool IsEditing
        {
            get { return _isEditing; }
            set { _isEditing = value; RaisePropertyChangedAuto(); }
        }


        /// <summary>
        /// Clones this instance.
        /// </summary>
        /// <returns></returns>
        public override TechItem Clone()
        {
            HeaterItem cloned = base.Clone() as HeaterItem;
            cloned.TechHeater = TechHeater;
            return cloned;
        }
    }
}
textView_VisualLineConstructionStarting; OnDocumentChanged(textView); } /// <inheritdoc/> protected override void OnRemoveFromTextView(TextView textView) { base.OnRemoveFromTextView(textView); textView.Services.RemoveService(typeof(IHighlighter)); textView.Services.RemoveService(typeof(DocumentHighlighter)); textView.DocumentChanged -= textView_DocumentChanged; textView.VisualLineConstructionStarting -= textView_VisualLineConstructionStarting; } void textView_VisualLineConstructionStarting(object sender, VisualLineConstructionStartEventArgs e) { IHighlighter highlighter = ((TextView)sender).Services.GetService(typeof(IHighlighter)) as IHighlighter; if (highlighter != null) { // Force update of highlighting state up to the position where we start generating visual lines. // This is necessary in case the document gets modified above the FirstLineInView so that the highlighting state changes. // We need to detect this case and issue a redraw (through TextViewDocumentHighligher.OnHighlightStateChanged) // before the visual line construction reuses existing lines that were built using the invalid highlighting state. lineNumberBeingColorized = e.FirstLineInView.LineNumber - 1; highlighter.GetSpanStack(lineNumberBeingColorized); lineNumberBeingColorized = 0; } } DocumentLine lastColorizedLine; /// <inheritdoc/> protected override void Colorize(ITextRunConstructionContext context) { this.lastColorizedLine = null; base.Colorize(context); if (this.lastColorizedLine != context.VisualLine.LastDocumentLine) { IHighlighter highlighter = context.TextView.Services.GetService(typeof(IHighlighter)) as IHighlighter; if (highlighter != null) { // In some cases, it is possible that we didn't highlight the last document line within the visual line // (e.g. when the line ends with a fold marker). // But even if we didn't highlight it, we'll have to update the highlighting state for it so that the // proof inside TextViewDocumentHighlighter.OnHighlightStateChanged holds. lineNumberBeingColorized = context.VisualLine.LastDocumentLine.LineNumber; highlighter.GetSpanStack(lineNumberBeingColorized); lineNumberBeingColorized = 0; } } this.lastColorizedLine = null; } int lineNumberBeingColorized; /// <inheritdoc/> protected override void ColorizeLine(DocumentLine line) { IHighlighter highlighter = CurrentContext.TextView.Services.GetService(typeof(IHighlighter)) as IHighlighter; if (highlighter != null) { lineNumberBeingColorized = line.LineNumber; HighlightedLine hl = highlighter.HighlightLine(lineNumberBeingColorized); lineNumberBeingColorized = 0; foreach (HighlightedSection section in hl.Sections) { if (IsEmptyColor(section.Color)) continue; ChangeLinePart(section.Offset, section.Offset + section.Length, visualLineElement => ApplyColorToElement(visualLineElement, section.Color)); } } this.lastColorizedLine = line; } /// <summary> /// Gets whether the color is empty (has no effect on a VisualLineTextElement). /// For example, the C# "Punctuation" is an empty color. /// </summary> bool IsEmptyColor(HighlightingColor color) { if (color == null) return true; return color.Background == null && color.Foreground == null && color.FontStyle == null && color.FontWeight == null; } /// <summary> /// Applies a highlighting color to a visual line element. /// </summary> protected virtual void ApplyColorToElement(VisualLineElement element, HighlightingColor color) { if (color.Foreground != null) { Brush b = color.Foreground.GetBrush(CurrentContext); if (b != null) element.TextRunProperties.SetForegroundBrush(b); } if (color.Background != null) { Brush b = color.Background.GetBrush(CurrentContext); if (b != null) element.BackgroundBrush = b; } if (color.FontStyle != null || color.FontWeight != null) { Typeface tf = element.TextRunProperties.Typeface; element.TextRunProperties.SetTypeface(new Typeface( tf.FontFamily, color.FontStyle ?? tf.Style, color.FontWeight ?? tf.Weight, tf.Stretch )); } } /// <summary> /// This class is responsible for telling the TextView to redraw lines when the highlighting state has changed. /// </summary> /// <remarks> /// Creation of a VisualLine triggers the syntax highlighter (which works on-demand), so it says: /// Hey, the user typed "/*". Don't just recreate that line, but also the next one /// because my highlighting state (at end of line) changed! /// </remarks> sealed class TextViewDocumentHighlighter : DocumentHighlighter { readonly HighlightingColorizer colorizer; readonly TextView textView; public TextViewDocumentHighlighter(HighlightingColorizer colorizer, TextView textView, TextDocument document, HighlightingRuleSet baseRuleSet) : base(document, baseRuleSet) { Debug.Assert(colorizer != null); Debug.Assert(textView != null); this.colorizer = colorizer; this.textView = textView; } protected override void OnHighlightStateChanged(DocumentLine line, int lineNumber) { base.OnHighlightStateChanged(line, lineNumber); if (colorizer.lineNumberBeingColorized != lineNumber) { // Ignore notifications for any line except the one we're interested in. // This improves the performance as Redraw() can take quite some time when called repeatedly // while scanning the document (above the visible area) for highlighting changes. return; } if (textView.Document != this.Document) { // May happen if document on text view was changed but some user code is still using the // existing IHighlighter instance. return; } // The user may have inserted "/*" into the current line, and so far only that line got redrawn. // So when the highlighting state is changed, we issue a redraw for the line immediately below. // If the highlighting state change applies to the lines below, too, the construction of each line // will invalidate the next line, and the construction pass will regenerate all lines. Debug.WriteLine("OnHighlightStateChanged forces redraw of line " + (lineNumber + 1)); // If the VisualLine construction is in progress, we have to avoid sending redraw commands for // anything above the line currently being constructed. // It takes some explanation to see why this cannot happen. // VisualLines always get constructed from top to bottom. // Each VisualLine construction calls into the highlighter and thus forces an update of the // highlighting state for all lines up to the one being constructed. // To guarantee that we don't redraw lines we just constructed, we need to show that when // a VisualLine is being reused, the highlighting state at that location is still up-to-date. // This isn't exactly trivial and the initial implementation was incorrect in the presence of external document changes // (e.g. split view). // For the first line in the view, the TextView.VisualLineConstructionStarting event is used to check that the // highlighting state is up-to-date. If it isn't, this method will be executed, and it'll mark the first line // in the view as requiring a redraw. This is safely possible because that event occurs before any lines are reused. // Once we take care of the first visual line, we won't get in trouble with other lines due to the top-to-bottom // construction process. // We'll prove that: if line N is being reused, then the highlighting state is up-to-date until (end of) line N-1. // Start of induction: the first line in view is reused only if the highlighting state was up-to-date // until line N-1 (no change detected in VisualLineConstructionStarting event). // Induction step: // If another line N+1 is being reused, then either // a) the previous line (the visual line containing document line N) was newly constructed // or b) the previous line was reused // In case a, the construction updated the highlighting state. This means the stack at end of line N is up-to-date. // In case b, the highlighting state at N-1 was up-to-date, and the text of line N was not changed. // (if the text was changed, the line could not have been reused). // From this follows that the highlighting state at N is still up-to-date. // The above proof holds even in the presence of folding: folding only ever hides text in the middle of a visual line. // Our Colorize-override ensures that the highlighting state is always updated for the LastDocumentLine, // so it will always invalidate the next visual line when a folded line is constructed // and the highlighting stack has changed. textView.Redraw(line.NextLine, DispatcherPriority.Normal); /* * Meta-comment: "why does this have to be so complicated?" * * The problem is that I want to re-highlight only on-demand and incrementally; * and at the same time only repaint changed lines. * So the highlighter and the VisualLine construction both have to run in a single pass. * The highlighter must take care that it never touches already constructed visual lines; * if it detects that something must be redrawn because the highlighting state changed, * it must do so early enough in the construction process. * But doing it too early means it doesn't have the information necessary to re-highlight and redraw only the desired parts. */ } } } }