Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb8d7993

Browse files
authored
1 parent29943c8 commitb8d7993

File tree

4 files changed

+187
-35
lines changed

4 files changed

+187
-35
lines changed

‎App/ViewModels/AgentViewModel.cs

Lines changed: 124 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
usingSystem;
2-
usingSystem.Collections.Generic;
3-
usingSystem.Collections.ObjectModel;
4-
usingSystem.ComponentModel;
5-
usingSystem.Linq;
6-
usingSystem.Threading;
7-
usingSystem.Threading.Tasks;
8-
usingWindows.ApplicationModel.DataTransfer;
91
usingCoder.Desktop.App.Services;
102
usingCoder.Desktop.App.Utils;
113
usingCoder.Desktop.CoderSdk;
@@ -18,15 +10,24 @@
1810
usingMicrosoft.UI.Xaml;
1911
usingMicrosoft.UI.Xaml.Controls;
2012
usingMicrosoft.UI.Xaml.Controls.Primitives;
13+
usingSystem;
14+
usingSystem.Collections.Generic;
15+
usingSystem.Collections.ObjectModel;
16+
usingSystem.ComponentModel;
17+
usingSystem.Linq;
18+
usingSystem.Text;
19+
usingSystem.Threading;
20+
usingSystem.Threading.Tasks;
21+
usingSystem.Xml.Linq;
22+
usingWindows.ApplicationModel.DataTransfer;
2123

2224
namespaceCoder.Desktop.App.ViewModels;
2325

2426
publicinterfaceIAgentViewModelFactory
2527
{
2628
publicAgentViewModelCreate(IAgentExpanderHostexpanderHost,Uuidid,stringfullyQualifiedDomainName,
27-
stringhostnameSuffix,
28-
AgentConnectionStatusconnectionStatus,UridashboardBaseUrl,string?workspaceName);
29-
29+
stringhostnameSuffix,AgentConnectionStatusconnectionStatus,UridashboardBaseUrl,
30+
string?workspaceName,bool?didP2p,string?preferredDerp,TimeSpan?latency,TimeSpan?preferredDerpLatency,DateTime?lastHandshake);
3031
publicAgentViewModelCreateDummy(IAgentExpanderHostexpanderHost,Uuidid,
3132
stringhostnameSuffix,
3233
AgentConnectionStatusconnectionStatus,UridashboardBaseUrl,stringworkspaceName);
@@ -40,7 +41,9 @@ public class AgentViewModelFactory(
4041
{
4142
publicAgentViewModelCreate(IAgentExpanderHostexpanderHost,Uuidid,stringfullyQualifiedDomainName,
4243
stringhostnameSuffix,
43-
AgentConnectionStatusconnectionStatus,UridashboardBaseUrl,string?workspaceName)
44+
AgentConnectionStatusconnectionStatus,UridashboardBaseUrl,
45+
string?workspaceName,bool?didP2p,string?preferredDerp,TimeSpan?latency,TimeSpan?preferredDerpLatency,
46+
DateTime?lastHandshake)
4447
{
4548
returnnewAgentViewModel(childLogger,coderApiClientFactory,credentialManager,agentAppViewModelFactory,
4649
expanderHost,id)
@@ -51,6 +54,11 @@ public AgentViewModel Create(IAgentExpanderHost expanderHost, Uuid id, string fu
5154
ConnectionStatus=connectionStatus,
5255
DashboardBaseUrl=dashboardBaseUrl,
5356
WorkspaceName=workspaceName,
57+
DidP2p=didP2p,
58+
PreferredDerp=preferredDerp,
59+
Latency=latency,
60+
PreferredDerpLatency=preferredDerpLatency,
61+
LastHandshake=lastHandshake,
5462
};
5563
}
5664

@@ -73,10 +81,25 @@ public AgentViewModel CreateDummy(IAgentExpanderHost expanderHost, Uuid id,
7381

7482
publicenumAgentConnectionStatus
7583
{
76-
Green,
77-
Yellow,
78-
Red,
79-
Gray,
84+
Healthy,
85+
Connecting,
86+
Unhealthy,
87+
NoRecentHandshake,
88+
Offline
89+
}
90+
91+
publicstaticclassAgentConnectionStatusExtensions
92+
{
93+
publicstaticstringToDisplayString(thisAgentConnectionStatusstatus)=>
94+
statusswitch
95+
{
96+
AgentConnectionStatus.Healthy=>"Healthy",
97+
AgentConnectionStatus.Connecting=>"Connecting",
98+
AgentConnectionStatus.Unhealthy=>"High latency",
99+
AgentConnectionStatus.NoRecentHandshake=>"No recent handshake",
100+
AgentConnectionStatus.Offline=>"Offline",
101+
_=>status.ToString()
102+
};
80103
}
81104

82105
publicpartialclassAgentViewModel:ObservableObject,IModelUpdateable<AgentViewModel>
@@ -160,6 +183,7 @@ public string FullyQualifiedDomainName
160183
[ObservableProperty]
161184
[NotifyPropertyChangedFor(nameof(ShowExpandAppsMessage))]
162185
[NotifyPropertyChangedFor(nameof(ExpandAppsMessage))]
186+
[NotifyPropertyChangedFor(nameof(ConnectionTooltip))]
163187
publicrequiredpartialAgentConnectionStatusConnectionStatus{get;set;}
164188

165189
[ObservableProperty]
@@ -182,6 +206,77 @@ public string FullyQualifiedDomainName
182206
[NotifyPropertyChangedFor(nameof(ExpandAppsMessage))]
183207
publicpartialboolAppFetchErrored{get;set;}=false;
184208

209+
[ObservableProperty]
210+
[NotifyPropertyChangedFor(nameof(ConnectionTooltip))]
211+
publicpartialbool?DidP2p{get;set;}=false;
212+
213+
[ObservableProperty]
214+
[NotifyPropertyChangedFor(nameof(ConnectionTooltip))]
215+
publicpartialstring?PreferredDerp{get;set;}=null;
216+
217+
[ObservableProperty]
218+
[NotifyPropertyChangedFor(nameof(ConnectionTooltip))]
219+
publicpartialTimeSpan?Latency{get;set;}=null;
220+
221+
[ObservableProperty]
222+
[NotifyPropertyChangedFor(nameof(ConnectionTooltip))]
223+
publicpartialTimeSpan?PreferredDerpLatency{get;set;}=null;
224+
225+
[ObservableProperty]
226+
[NotifyPropertyChangedFor(nameof(ConnectionTooltip))]
227+
publicpartialDateTime?LastHandshake{get;set;}=null;
228+
229+
publicstringConnectionTooltip
230+
{
231+
get
232+
{
233+
vardescription=newStringBuilder();
234+
varhighLatencyWarning=ConnectionStatus==AgentConnectionStatus.Unhealthy?$"({AgentConnectionStatus.Unhealthy.ToDisplayString()})":"";
235+
236+
if(DidP2p!=null&&DidP2p.Value&&Latency!=null)
237+
{
238+
description.Append($"""
239+
You're connected peer-to-peer.{highLatencyWarning}
240+
241+
You ↔{Latency.Value.Milliseconds} ms ↔{WorkspaceName}
242+
"""
243+
);
244+
}
245+
elseif(Latency!=null)
246+
{
247+
description.Append($"""
248+
You're connected through a DERP relay.{highLatencyWarning}
249+
We'll switch over to peer-to-peer when available.
250+
251+
Total latency:{Latency.Value.Milliseconds} ms
252+
"""
253+
);
254+
255+
if(PreferredDerpLatency!=null)
256+
{
257+
description.Append($"\nYou ↔{PreferredDerp}:{PreferredDerpLatency.Value.Milliseconds} ms");
258+
259+
varderpToWorkspaceEstimatedLatency=Latency-PreferredDerpLatency;
260+
261+
// Guard against negative values if the two readings were taken at different times
262+
if(derpToWorkspaceEstimatedLatency>TimeSpan.Zero)
263+
{
264+
description.Append($"\n{PreferredDerp} ms ↔{WorkspaceName}:{derpToWorkspaceEstimatedLatency.Value.Milliseconds} ms");
265+
}
266+
}
267+
}
268+
else
269+
{
270+
description.Append(ConnectionStatus.ToDisplayString());
271+
}
272+
if(LastHandshake!=null)
273+
description.Append($"\n\nLast handshake:{LastHandshake?.ToString()}");
274+
275+
returndescription.ToString().TrimEnd('\n',' ');;
276+
}
277+
}
278+
279+
185280
// We only show 6 apps max, which fills the entire width of the tray
186281
// window.
187282
publicIEnumerable<AgentAppViewModel>VisibleApps=>Apps.Count>MaxAppsPerRow?Apps.Take(MaxAppsPerRow):Apps;
@@ -192,7 +287,7 @@ public string? ExpandAppsMessage
192287
{
193288
get
194289
{
195-
if(ConnectionStatus==AgentConnectionStatus.Gray)
290+
if(ConnectionStatus==AgentConnectionStatus.Offline)
196291
return"Your workspace is offline.";
197292
if(FetchingApps&&Apps.Count==0)
198293
// Don't show this message if we have any apps already. When
@@ -285,6 +380,16 @@ public bool TryApplyChanges(AgentViewModel model)
285380
DashboardBaseUrl=model.DashboardBaseUrl;
286381
if(WorkspaceName!=model.WorkspaceName)
287382
WorkspaceName=model.WorkspaceName;
383+
if(DidP2p!=model.DidP2p)
384+
DidP2p=model.DidP2p;
385+
if(PreferredDerp!=model.PreferredDerp)
386+
PreferredDerp=model.PreferredDerp;
387+
if(Latency!=model.Latency)
388+
Latency=model.Latency;
389+
if(PreferredDerpLatency!=model.PreferredDerpLatency)
390+
PreferredDerpLatency=model.PreferredDerpLatency;
391+
if(LastHandshake!=model.LastHandshake)
392+
LastHandshake=model.LastHandshake;
288393

289394
// Apps are not set externally.
290395

@@ -307,7 +412,7 @@ public void SetExpanded(bool expanded)
307412

308413
partialvoidOnConnectionStatusChanged(AgentConnectionStatusoldValue,AgentConnectionStatusnewValue)
309414
{
310-
if(IsExpanded&&newValueis notAgentConnectionStatus.Gray)FetchApps();
415+
if(IsExpanded&&newValueis notAgentConnectionStatus.Offline)FetchApps();
311416
}
312417

313418
privatevoidFetchApps()
@@ -316,7 +421,7 @@ private void FetchApps()
316421
FetchingApps=true;
317422

318423
// If the workspace is off, then there's no agent and there's no apps.
319-
if(ConnectionStatus==AgentConnectionStatus.Gray)
424+
if(ConnectionStatus==AgentConnectionStatus.Offline)
320425
{
321426
FetchingApps=false;
322427
Apps.Clear();

‎App/ViewModels/TrayWindowViewModel.cs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
usingSystem.Collections.ObjectModel;
44
usingSystem.ComponentModel;
55
usingSystem.Linq;
6+
usingSystem.Security.Principal;
67
usingSystem.Threading.Tasks;
78
usingCoder.Desktop.App.Models;
89
usingCoder.Desktop.App.Services;
@@ -29,6 +30,7 @@ public partial class TrayWindowViewModel : ObservableObject, IAgentExpanderHost
2930
{
3031
privateconstintMaxAgents=5;
3132
privateconststringDefaultDashboardUrl="https://coder.com";
33+
privatereadonlyTimeSpanHealthyPingThreshold=TimeSpan.FromMilliseconds(150);
3234

3335
privatereadonlyIServiceProvider_services;
3436
privatereadonlyIRpcController_rpcController;
@@ -222,10 +224,28 @@ private void UpdateFromRpcModel(RpcModel rpcModel)
222224
if(string.IsNullOrWhiteSpace(fqdn))
223225
continue;
224226

225-
varlastHandshakeAgo=DateTime.UtcNow.Subtract(agent.LastHandshake.ToDateTime());
226-
varconnectionStatus=lastHandshakeAgo<TimeSpan.FromMinutes(5)
227-
?AgentConnectionStatus.Green
228-
:AgentConnectionStatus.Yellow;
227+
varconnectionStatus=AgentConnectionStatus.Healthy;
228+
229+
if(agent.LastHandshake!=null&&agent.LastHandshake.ToDateTime()!=default&&agent.LastHandshake.ToDateTime()<DateTime.UtcNow)
230+
{
231+
// For compatibility with older deployments, we assume that if the
232+
// last ping is null, the agent is healthy.
233+
varisLatencyAcceptable=agent.LastPing==null||agent.LastPing.Latency.ToTimeSpan()<HealthyPingThreshold;
234+
235+
varlastHandshakeAgo=DateTime.UtcNow.Subtract(agent.LastHandshake.ToDateTime());
236+
237+
if(lastHandshakeAgo>TimeSpan.FromMinutes(5))
238+
connectionStatus=AgentConnectionStatus.NoRecentHandshake;
239+
elseif(!isLatencyAcceptable)
240+
connectionStatus=AgentConnectionStatus.Unhealthy;
241+
}
242+
else
243+
{
244+
// If the last handshake is not correct (null, default or in the future),
245+
// we assume the agent is connecting (yellow status icon).
246+
connectionStatus=AgentConnectionStatus.Connecting;
247+
}
248+
229249
workspacesWithAgents.Add(agent.WorkspaceId);
230250
varworkspace=rpcModel.Workspaces.FirstOrDefault(w=>w.Id==agent.WorkspaceId);
231251

@@ -236,7 +256,12 @@ private void UpdateFromRpcModel(RpcModel rpcModel)
236256
_hostnameSuffixGetter.GetCachedSuffix(),
237257
connectionStatus,
238258
credentialModel.CoderUrl,
239-
workspace?.Name));
259+
workspace?.Name,
260+
agent.LastPing?.DidP2P,
261+
agent.LastPing?.PreferredDerp,
262+
agent.LastPing?.Latency?.ToTimeSpan(),
263+
agent.LastPing?.PreferredDerpLatency?.ToTimeSpan(),
264+
agent.LastHandshake!=null&&agent.LastHandshake.ToDateTime()!=default?agent.LastHandshake?.ToDateTime():null));
240265
}
241266

242267
// For every stopped workspace that doesn't have any agents, add a
@@ -253,7 +278,7 @@ private void UpdateFromRpcModel(RpcModel rpcModel)
253278
// conflict with any agent IDs.
254279
uuid,
255280
_hostnameSuffixGetter.GetCachedSuffix(),
256-
AgentConnectionStatus.Gray,
281+
AgentConnectionStatus.Offline,
257282
credentialModel.CoderUrl,
258283
workspace.Name));
259284
}
@@ -268,7 +293,7 @@ private void UpdateFromRpcModel(RpcModel rpcModel)
268293

269294
if(Agents.Count<MaxAgents)ShowAllAgents=false;
270295

271-
varfirstOnlineAgent=agents.FirstOrDefault(a=>a.ConnectionStatus!=AgentConnectionStatus.Gray);
296+
varfirstOnlineAgent=agents.FirstOrDefault(a=>a.ConnectionStatus!=AgentConnectionStatus.Offline);
272297
if(firstOnlineAgentisnull)
273298
_hasExpandedAgent=false;
274299
if(!_hasExpandedAgent&&firstOnlineAgentis notnull)
@@ -433,7 +458,7 @@ private static bool ShouldShowDummy(Workspace workspace)
433458
caseWorkspace.Types.Status.Stopping:
434459
caseWorkspace.Types.Status.Stopped:
435460
returntrue;
436-
// TODO: should we include and show a different color thanGray for workspaces that are
461+
// TODO: should we include and show a different color thanOffline for workspaces that are
437462
// failed, canceled or deleting?
438463
default:
439464
returnfalse;

‎App/Views/Pages/TrayWindowMainPage.xaml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,22 +137,27 @@
137137
x:Key="StatusColor"
138138
SelectedKey="{x:Bind Path=ConnectionStatus, Mode=OneWay}">
139139

140-
<converters:StringToBrushSelectorItem>
140+
<converters:StringToBrushSelectorItemKey="Offline">
141141
<converters:StringToBrushSelectorItem.Value>
142142
<SolidColorBrushColor="#8e8e93" />
143143
</converters:StringToBrushSelectorItem.Value>
144144
</converters:StringToBrushSelectorItem>
145-
<converters:StringToBrushSelectorItemKey="Red">
145+
<converters:StringToBrushSelectorItemKey="NoRecentHandshake">
146146
<converters:StringToBrushSelectorItem.Value>
147147
<SolidColorBrushColor="#ff3b30" />
148148
</converters:StringToBrushSelectorItem.Value>
149149
</converters:StringToBrushSelectorItem>
150-
<converters:StringToBrushSelectorItemKey="Yellow">
150+
<converters:StringToBrushSelectorItemKey="Unhealthy">
151151
<converters:StringToBrushSelectorItem.Value>
152152
<SolidColorBrushColor="#ffcc01" />
153153
</converters:StringToBrushSelectorItem.Value>
154154
</converters:StringToBrushSelectorItem>
155-
<converters:StringToBrushSelectorItemKey="Green">
155+
<converters:StringToBrushSelectorItemKey="Connecting">
156+
<converters:StringToBrushSelectorItem.Value>
157+
<SolidColorBrushColor="#ffcc01" />
158+
</converters:StringToBrushSelectorItem.Value>
159+
</converters:StringToBrushSelectorItem>
160+
<converters:StringToBrushSelectorItemKey="Healthy">
156161
<converters:StringToBrushSelectorItem.Value>
157162
<SolidColorBrushColor="#34c759" />
158163
</converters:StringToBrushSelectorItem.Value>
@@ -189,6 +194,7 @@
189194
HorizontalAlignment="Right"
190195
VerticalAlignment="Center"
191196
Height="14"Width="14"
197+
ToolTipService.ToolTip="{x:Bind ConnectionTooltip, Mode=OneWay}"
192198
Margin="0,1,0,0">
193199

194200
<Ellipse

‎Vpn.Proto/vpn.proto

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ option go_package = "github.com/coder/coder/v2/vpn";
33
optioncsharp_namespace="Coder.Desktop.Vpn.Proto";
44

55
import"google/protobuf/timestamp.proto";
6+
import"google/protobuf/duration.proto";
67

78
packagevpn;
89

@@ -48,10 +49,10 @@ message TunnelMessage {
4849
messageClientMessage {
4950
RPCrpc=1;
5051
oneofmsg {
51-
StartRequeststart=2;
52-
StopRequeststop=3;
53-
StatusRequeststatus=4;
54-
}
52+
StartRequeststart=2;
53+
StopRequeststop=3;
54+
StatusRequeststatus=4;
55+
}
5556
}
5657

5758
// ServiceMessage is a message from the service (to the client). Windows only.
@@ -131,6 +132,21 @@ message Agent {
131132
// last_handshake is the primary indicator of whether we are connected to a peer. Zero value or
132133
// anything longer than 5 minutes ago means there is a problem.
133134
google.protobuf.Timestamplast_handshake=6;
135+
// If unset, a successful ping has not yet been made.
136+
optionalLastPinglast_ping=7;
137+
}
138+
139+
messageLastPing {
140+
// latency is the RTT of the ping to the agent.
141+
google.protobuf.Durationlatency=1;
142+
// did_p2p indicates whether the ping was sent P2P, or over DERP.
143+
booldid_p2p=2;
144+
// preferred_derp is the human readable name of the preferred DERP region,
145+
// or the region used for the last ping, if it was sent over DERP.
146+
stringpreferred_derp=3;
147+
// preferred_derp_latency is the last known latency to the preferred DERP
148+
// region. Unset if the region does not appear in the DERP map.
149+
optionalgoogle.protobuf.Durationpreferred_derp_latency=4;
134150
}
135151

136152
// NetworkSettingsRequest is based on

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp