I have a class called LinearTransformation whose values I want to set during execution so that they are used next time, and then can be overwritten, etc.
Currently, I am verifying if there is a previous value and, if not, I set that value to a default one.
The problem is: my code has become repetitive, ugly, and most of it will only be useful the first time a new installation is run on a client machine.
Is there a more elegant way to achieve this?
// This method is run when the app starts
private void LoadCalibrações()
{
if (Properties.Settings.Default.CalibXEsq == null)
{
Properties.Settings.Default.CalibXEsq = new TransformaçãoLinear();
}
if (Properties.Settings.Default.CalibYEsq == null)
{
Properties.Settings.Default.CalibYEsq = new TransformaçãoLinear();
}
if (Properties.Settings.Default.CalibXDir == null)
{
Properties.Settings.Default.CalibXDir = new TransformaçãoLinear();
}
if (Properties.Settings.Default.CalibYDir == null)
{
Properties.Settings.Default.CalibYDir = new TransformaçãoLinear();
}
Properties.Settings.Default.Save();
_calibrações = new[]
{
Properties.Settings.Default.CalibXEsq,
Properties.Settings.Default.CalibYEsq,
Properties.Settings.Default.CalibXDir,
Properties.Settings.Default.CalibYDir
};
}
If you just need to fill the array...
var settings = Properties.Settings.Default;
_calibrações = new[]
{
settings.CalibXEsq ?? new TransformaçãoLinear(),
settings.CalibYEsq ?? new TransformaçãoLinear(),
settings.CalibXDir ?? new TransformaçãoLinear(),
settings.CalibYDir ?? new TransformaçãoLinear(),
};
otherwise...
var settings = Properties.Settings.Default;
_calibrações = new[]
{
settings.CalibXEsq ?? (settings.CalibXEsq = new TransformaçãoLinear()),
settings.CalibYEsq ?? (settings.CalibYEsq = new TransformaçãoLinear()),
settings.CalibXDir ?? (settings.CalibXDir = new TransformaçãoLinear()),
settings.CalibYDir ?? (settings.CalibYDir = new TransformaçãoLinear()),
};
Related
I'm working with TwincatAds.Reactive 6.0.190 in .NET 6 WPF Desktop application.
I'm also using MVVM pattern.
My goal is to create a Class that is going to observe for a PLC Variable changes, collect those variables to a dictionary, and later on use those values in the ViewModel.
Here's the method where I'm attaching the notification and action where I'm handling the notification.
public void AttachNotification(IEnumerable<(string key, Type type)> Symbols)
{
_observerValueNotification = Observer.Create<ValueNotification>(val =>
{
// Does handle really start from 2?
var handle = val.Handle;
if (val.UserData is object[] objects)
{
string tag = objects[handle - 2].ToString();
if (!_values.Any(x => x.Key == tag))
_values.Add(new SymbolModel { Key = tag, Value = val.Value });
else
{
var symbol = _values.First(x => x.Key == tag);
symbol.Value = val.Value;
}
}
ValuesChanged?.Invoke(_values);
});
if (_plcWrapper.AdsClient != null)
{
// Get Symbols from SymbolLoader
List<AnySymbolSpecifier> list = new();
List<string> userData = new();
foreach (var (key, type) in Symbols)
{
list.Add(new AnySymbolSpecifier(key, new AnyTypeSpecifier(type)));
userData.Add(key);
}
_subscription2 = _plcWrapper.AdsClient.WhenNotificationEx(list, NotificationSettings.ImmediatelyOnChange, userData.ToArray())
.Subscribe(_observerValueNotification);
}
}
I'm using ValueNotification simply because, I'd like to use this pattern also for complex PLC Variables like Structs.
As You can see, in the WhenNotificationEx method I'm using UserData[] to provide some sort of identification of what Variable has changed when handling the change.
My idea was to use Handle property from ValueNotification as an indexer in UserData[] to identify what variable I'm dealing with, but for some reason Handle starts from 2.
My question is, is it expected behaviour, does the Handle value really always start from 2?
I've decided that relying on the Handle being index in the UserData array is quite unpredictable as Handle is being created by the Twincat Ads server.
Solved the issue by creating own extension method to the WhenNotificationEx. Turned out IDisposableHandleBag has exactly what I was looking for, which is SourceResultHandles property, where AnySymbolSpecifier and ResultHandle are both stored!
Here's created extension method
public static Dictionary<string, uint> Handles { get; private set; } = new();
public static IObservable<ValueNotification> WhenNotificationWithHandle(this IAdsConnection connection, IList<AnySymbolSpecifier> symbols, NotificationSettings settings)
{
IAdsConnection connection2 = connection;
IList<AnySymbolSpecifier> symbols2 = symbols;
NotificationSettings settings2 = settings;
if (connection2 == null)
{
throw new ArgumentNullException("connection");
}
if (symbols2 == null)
{
throw new ArgumentNullException("symbols");
}
if (symbols2.Count == 0)
{
throw new ArgumentOutOfRangeException("symbols", "Symbol list is empty!");
}
IDisposableHandleBag<AnySymbolSpecifier> bag = null;
EventLoopScheduler scheduler = new EventLoopScheduler();
IObservable<int> whenSymbolChangeObserver = connection2.WhenSymbolVersionChanges(scheduler);
IDisposable whenSymbolChanges = null;
Action<EventHandler<AdsNotificationExEventArgs>> addHandler = delegate (EventHandler<AdsNotificationExEventArgs> h)
{
connection2.AdsNotificationEx += h;
bag = ((IAdsHandleCacheProvider)connection2).CreateNotificationExHandleBag(symbols2, relaxSubErrors: false, settings2, null);
bag.CreateHandles();
// Collect Handles
Handles.Clear();
foreach (var item in bag.SourceResultHandles)
Handles.Add(item.source.InstancePath, item.result.Handle);
whenSymbolChanges = whenSymbolChangeObserver.Subscribe((Action<int>)delegate
{
bag.CreateHandles();
Handles.Clear();
foreach (var item in bag.SourceResultHandles)
Handles.Add(item.source.InstancePath, item.result.Handle);
}, (Action<Exception>)delegate
{
TcTraceSource traceAds = AdsModule.TraceAds;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(101, 1);
defaultInterpolatedStringHandler.AppendLiteral("The AdsServer '");
defaultInterpolatedStringHandler.AppendFormatted(connection2.Address);
defaultInterpolatedStringHandler.AppendLiteral("' doesn't support SymbolVersionChanged Notifications! Handle recreation is not active!");
traceAds.TraceInformation(defaultInterpolatedStringHandler.ToStringAndClear());
});
};
Action<EventHandler<AdsNotificationExEventArgs>> removeHandler = delegate (EventHandler<AdsNotificationExEventArgs> h)
{
if (whenSymbolChanges != null)
{
whenSymbolChanges.Dispose();
}
scheduler.Dispose();
if (bag != null)
{
bag.Dispose();
bag = null;
Handles.Clear();
}
connection2.AdsNotificationEx -= h;
};
return from ev in Observable.FromEventPattern<EventHandler<AdsNotificationExEventArgs>, AdsNotificationExEventArgs>(addHandler, removeHandler)
where bag.Contains(ev.EventArgs.Handle)
select new ValueNotification(ev.EventArgs, ev.EventArgs.Value);
}
I am trying to add a new variable to an existing Devops ReleaseEnvironment and redeploy this existing releaseEnvironment.
I'm using Microsoft.VisualStudio.Services.Release.Client version 16.199.0-preview.
var connection = new VssConnection("someUrl", new VssBasicCredential(string.Empty, "somePAT"));
var client = connection.GetClient<ReleaseHttpClient>();
var projectHttpClient = connection.GetClient<ProjectHttpClient>();
var project = await projectHttpClient.GetProject("xxx");
var metadata = new ReleaseEnvironmentUpdateMetadata
{
Status = EnvironmentStatus.InProgress
};
metadata.Variables.Add("someVariable",
new ConfigurationVariableValue()
{
Value = "xxx",
AllowOverride = true,
IsSecret = true
});
await client.UpdateReleaseEnvironmentAsync(metadata, project.Id, 999,999);
However, cause this variable does not exist yet, I'm getting the error:
Unhandled exception. Microsoft.VisualStudio.Services.Common.VssServiceException: Variable(s) someVariable do not exist in the release environment at scope: PRD. New variables cannot be added while creating deployment.
However it looks like it's possible to do it via the UI. So I was wondering how I can automate this. Does anyone have an idea?
Thx in advance.
Best regards,
JeffVN
Found the solution.
You'll just need to fetch the Release and update the variables in the Environment:
var connection = new VssConnection("someUrl", new VssBasicCredential(string.Empty, "somePAT"));
var client = connection.GetClient<ReleaseHttpClient>();
var project = await projectHttpClient.GetProject("xxx");
var release = await client.GetReleaseAsync(project.Id, 999);
if (release == null)
{
Console.WriteLine("Release 999 not found");
return;
}
var releaseEnv = release.Environments.FirstOrDefault(x => x.Id == 999);
if (releaseEnv == null)
{
Console.WriteLine("ReleaseEnvironment 999 not found");
return;
}
if (!releaseEnv.Variables.ContainsKey("someVariable"))
{
releaseEnv.Variables.Add("someVariable", new ConfigurationVariableValue()
{
Value = "xxx",
AllowOverride = true,
IsSecret = true
});
}
else
{
releaseEnv.Variables["someVariable"].Value = "xxx";
}
await client.UpdateReleaseAsync(release, project.Id, 999);
My team is developing an application that needs to clone existing templates in our vSphere environment. We're using VMware.Vim in a C# application to do this. We're replacing an already existing implementation that uses PowerShell.
Below is the code that is throwing the error. We are eventually going to load balance based on memory usage, but currently we are selecting the host by random. That's why there is some extra code with collecting all of the hosts and then picking one.
When it gets to CloneVM_Task, an exception with the message 'The operation is not allowed in the current state.' is thrown. The exception doesn't give me much to work with and I can't find any useful logs in vSphere. vSphere just says "An error prevented the virtual machine from being cloned" in the events log. We're using version 6.7. I'm new to VMWare, so any help is appreciated. Their documentation is lacking, to say the least.
public async void CreateVirtualMachineAsync(NewVMRequest newVMRequest)
{
var appliance = await _applianceService.GetAppliance(newVMRequest.Appliance);
var vimClient = new VimClientImpl
{
IgnoreServerCertificateErrors = true, ServiceUrl = appliance.ServiceUrl
};
vimClient.Login(appliance.User, appliance.Password);
var datacenter = GetDatacenter(vimClient);
var hostCollection = GetListOfHosts(vimClient, datacenter);
var randomHost = PickRandomHost(hostCollection);
var sourceVm = GetSelectedVm(vimClient, newVMRequest);
if (sourceVm == null)
{
_logger.LogDebug($"Could not find virtual machine {newVMRequest.Source} to use for template");
_logger.LogError($"Could not find virtual machine {newVMRequest.Source} to use for template", null);
return;
}
var selectedStore = ConnectToDataStore(vimClient);
var cluster = GetCluster(vimClient);
var mySpec = CreateCloneSpec(selectedStore, randomHost, cluster, sourceVm);
vimClient.WaitForTask(sourceVm.CloneVM_Task(sourceVm.Parent, newVMRequest.Name, mySpec));
vimClient.Disconnect();
}
private VirtualMachineCloneSpec CreateCloneSpec(Datastore selectedStore, ManagedObjectReference randomHost, ClusterComputeResource cluster, VirtualMachine sourceVm)
{
var mySpec = new VirtualMachineCloneSpec
{
Location = new VirtualMachineRelocateSpec
{
Datastore = selectedStore.MoRef,
Transform = VirtualMachineRelocateTransformation.sparse,
Host = randomHost,
Pool = cluster.ResourcePool
},
Config = new VirtualMachineConfigSpec()
};
var networkDevice = new VirtualDevice();
foreach (var vDevice in sourceVm.Config.Hardware.Device)
{
if (vDevice.DeviceInfo.Label.Contains("Network"))
{
networkDevice = vDevice;
}
}
var devSpec = new VirtualDeviceConfigSpec
{
Device = networkDevice, FileOperation = VirtualDeviceConfigSpecFileOperation.create
};
mySpec.Config.DeviceChange = new[] { devSpec };
return mySpec;
}
private Datacenter GetDatacenter(VimClient vimClient)
{
var entities = vimClient.FindEntityViews(typeof(Datacenter), null, null, null);
return (Datacenter)entities.First();
}
private List<ManagedObjectReference> GetListOfHosts(VimClient vimClient, Datacenter datacenter)
{
var hostCollection = new List<ManagedObjectReference>();
var hostFolderMoRef = datacenter.HostFolder;
var hostFolder = (Folder)vimClient.GetView(hostFolderMoRef, null);
var childEntityMoRefs = hostFolder.ChildEntity;
foreach (var childEntityMoRef in childEntityMoRefs)
{
var thisCluster = (ClusterComputeResource)vimClient.GetView(childEntityMoRef, null);
var clusterHostMoRefs = thisCluster.Host;
foreach (var clusterHostMoRef in clusterHostMoRefs)
{
var hostSystem = (HostSystem)vimClient.GetView(clusterHostMoRef, null);
hostCollection.Add(hostSystem.MoRef);
}
}
return hostCollection;
}
private ManagedObjectReference PickRandomHost(List<ManagedObjectReference> hostCollection)
{
var rand = new Random();
return hostCollection[rand.Next(0, hostCollection.Count)];
}
private VirtualMachine GetSelectedVm(VimClient vimClient, NewVMRequest newVMRequest)
{
var filter = new NameValueCollection
{
{"name", newVMRequest.Source},
{"Config.Template", newVMRequest.UseTemplate.ToString().ToLower()}
};
var entityViews = vimClient.FindEntityViews(typeof(VMware.Vim.VirtualMachine), null, filter, null);
if (entityViews.Count == 0)
{
return null;
}
return (VirtualMachine)vimClient.FindEntityViews(typeof(VMware.Vim.VirtualMachine), null, filter, null).First();
}
private Datastore ConnectToDataStore(VimClient vimClient)
{
var myDs = vimClient.FindEntityView(typeof(Datastore), null, null /*dsFilter*/, null);
return (Datastore)myDs;
}
private ClusterComputeResource GetCluster(VimClient vimClient)
{
var appClusters = vimClient.FindEntityViews(typeof(ClusterComputeResource), null, null, null);
return (ClusterComputeResource)appClusters?.FirstOrDefault();
}
For the most part, your code looks good. I would suggest changing the spec file and using the least possible information to create a clone from existing template. Once you have success, you can add more details and see if that errors out.
Start off with this and see if this gets you going.
private VirtualMachineCloneSpec CreateCloneSpec(Datastore selectedStore, ManagedObjectReference randomHost, ClusterComputeResource cluster, VirtualMachine sourceVm)
{
var mySpec = new VirtualMachineCloneSpec
{
Location = new VirtualMachineRelocateSpec
{
Datastore = selectedStore.MoRef,
Host = randomHost,
Pool = cluster.ResourcePool
},
PowerOn = false,
Template = false
};
return mySpec;
}
If the above goes through, then add the VirtualMachineRelocateTransformation to transform your thick VM as thin VM and network connections as needed.
I've using nekodrive library.
And i need to add functionality to change last modified date of file. Found method SetAttr. NFSv3.cs And apply similar to SetFileSize function to set modified date:
public void SetLastModifiedDate(string FileFullName, DateTime LastModified)
{
if (_ProtocolV3 == null)
{ throw new NFSConnectionException("NFS Client not connected!"); }
if (_MountProtocolV3 == null)
{ throw new NFSMountConnectionException("NFS Device not connected!"); }
NFSAttributes Attributes = GetItemAttributes(FileFullName);
SetAttributeArguments dpArgSAttr = new SetAttributeArguments();
NFSTimeValue lastModified = new NFSTimeValue();
lastModified.Seconds = (int)(LastModified - new DateTime(1970, 1, 1)).TotalSeconds;
dpArgSAttr.Handle = new NFSHandle(Attributes.Handle, V3.RPC.NFSv3Protocol.NFS_V3);
dpArgSAttr.Attributes = new MakeAttributes();
dpArgSAttr.Attributes.LastAccessedTime = new NFSTimeValue();
dpArgSAttr.Attributes.ModifiedTime = lastModified;
dpArgSAttr.Attributes.SetModifiedTime = TimeHow.SET_TO_CLIENT_TIME;
dpArgSAttr.Attributes.Mode = Attributes.Mode;
dpArgSAttr.Attributes.UserID = -1;
dpArgSAttr.Attributes.GroupID = -1;
dpArgSAttr.Attributes.SetSize = false;
dpArgSAttr.GuardCreateTime = new NFSTimeValue();
dpArgSAttr.GuardCheck = false;
ResultObject<SetAttributeAccessOK, SetAttributeAccessFAIL> pAttrStat =
_ProtocolV3.NFSPROC3_SETATTR(dpArgSAttr);
if (pAttrStat == null || pAttrStat.Status != NFSStats.NFS_OK)
{
if (pAttrStat == null)
{ throw new NFSGeneralException("NFSPROC3_SETATTR: failure"); }
ExceptionHelpers.ThrowException(pAttrStat.Status);
}
}
Unfortunately i get an error: Update synchronization mismatch was detected during a SETATTR operation. Error Code is NFSERR_NOT_SYNC = 10002
Exports file:
/home/nfs *(rw)
Can anyone advice on what could be wrong? may be just NFS configuration?
Here's what I'm doing:
Selector selector = new Selector();
selector.fields = new string[] {"CampaignId", "AdGroupId", "Id", "CriteriaType", "Criteria", "CriteriaDestinationUrl", "Clicks", "Impressions", "Cost"};
Predicate predicate = new Predicate();
predicate.field = "Status";
predicate.#operator = PredicateOperator.IN;
predicate.values = new string[] { "ACTIVE", "PAUSED" };
selector.predicates = new Predicate[] { predicate };
ReportDefinition definition = new ReportDefinition();
definition.reportName = "criteria report";
definition.reportType = ReportDefinitionReportType.CRITERIA_PERFORMANCE_REPORT;
definition.downloadFormat = DownloadFormat.XML;
definition.dateRangeType = ReportDefinitionDateRangeType.YESTERDAY;
definition.selector = selector;
definition.includeZeroImpressions = true;
_handler.RunReport(new AdWordsUser(), definition);
And here's my handler method:
public void RunReport(AdWordsUser user, ReportDefinition definition)
{
if (definition != null)
{
string reportContents = string.Empty;
try
{
ReportUtilities utilities = new ReportUtilities(user);
utilities.ReportVersion = "v201206";
ClientReport report = utilities.GetClientReport(definition);
reportContents = report.Contents.ToString();
}
catch (Exception ex)
{
_errorHandler.AddError(ex);
}
}
}
Where I step through I get this error:
Report contents are invalid. - !!!2|||-1|||[ReportDefinitionError.CUSTOMER_SERVING_TYPE_REPORT_MISMATCH # selector]???
Been searching for hours trying to find a solution. Any hints?
It appears that the issue is indeed needing to pass in the customerClientID.
This document helped me picture what was going on.
I ended up modifying my code like this:
var configOptions = new Dictionary<string,string>();
configOptions.Add("clientCustomerID", customerID.ToString());
_handler.RunReport(new AdWordsUser(configOptions), definition);