public static SlaveInfo ReloadHardware(string esiDirectoryPath, IExtensionFactory extensionFactory, string interfaceName, SlaveInfo referenceRootSlaveInfo) { IntPtr context; SlaveInfo newRootSlaveInfo; SlaveInfo referenceSlaveInfo; IEnumerable <SlaveInfo> referenceSlaveInfoSet; referenceSlaveInfo = null; referenceSlaveInfoSet = null; if (NetworkInterface.GetAllNetworkInterfaces().Where(x => x.GetPhysicalAddress().ToString() == interfaceName).FirstOrDefault()?.OperationalStatus != OperationalStatus.Up) { throw new Exception($"The network interface '{interfaceName}' is not linked. Aborting action."); } context = EcHL.CreateContext(); newRootSlaveInfo = EcUtilities.ScanDevices(context, interfaceName, referenceRootSlaveInfo); EcHL.FreeContext(context); if (referenceRootSlaveInfo != null) { referenceSlaveInfoSet = referenceRootSlaveInfo.Descendants().ToList(); } newRootSlaveInfo.Descendants().ToList().ForEach(slaveInfo => { referenceSlaveInfo = slaveInfo.Csa == slaveInfo.OldCsa ? referenceSlaveInfoSet?.FirstOrDefault(x => x.Csa == slaveInfo.Csa) : null; ExtensibilityHelper.GetDynamicSlaveInfoData(esiDirectoryPath, extensionFactory, slaveInfo); ExtensibilityHelper.UpdateSlaveExtensions(extensionFactory, slaveInfo, referenceSlaveInfo); }); return(newRootSlaveInfo); }
public SlaveVariable(SlavePdo parent, string name, ushort index, byte subIndex, DataDirection dataDirection, EthercatDataType dataType, byte bitLength = 0) { this.Parent = parent; this.Name = name; this.Index = index; this.SubIndex = subIndex; this.DataDirection = dataDirection; this.DataType = dataType; if (bitLength == 0) { this.BitLength = EcUtilities.GetBitLength(dataType); } else { this.BitLength = bitLength; } }
static async Task Main(string[] args) { /* Set interface name. Edit this to suit your needs. */ var interfaceName = "eth0"; /* Set ESI location. Make sure it contains ESI files! The default path is /home/{user}/.local/share/ESI */ var localAppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); var esiDirectoryPath = Path.Combine(localAppDataPath, "ESI"); Directory.CreateDirectory(esiDirectoryPath); /* Copy native file. NOT required in end user scenarios, where EtherCAT.NET package is installed via NuGet! */ var codeBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); Directory.EnumerateFiles(Path.Combine(codeBase, "runtimes"), "*soem_wrapper.*", SearchOption.AllDirectories).ToList().ForEach(filePath => { if (filePath.Contains(RuntimeEnvironment.RuntimeArchitecture)) { File.Copy(filePath, Path.Combine(codeBase, Path.GetFileName(filePath)), true); } }); /* prepare dependency injection */ var services = new ServiceCollection(); ConfigureServices(services); /* create types */ var provider = services.BuildServiceProvider(); var extensionFactory = provider.GetRequiredService <IExtensionFactory>(); var loggerFactory = provider.GetRequiredService <ILoggerFactory>(); var logger = loggerFactory.CreateLogger("EtherCAT Master"); /* create EtherCAT master settings (with 10 Hz cycle frequency) */ var cycleFrequency = 10U; var settings = new EcSettings(cycleFrequency, esiDirectoryPath, interfaceName); /* create root slave info by scanning available slaves */ var rootSlaveInfo = EcUtilities.ScanDevices(settings.InterfaceName); rootSlaveInfo.Descendants().ToList().ForEach(current => { ExtensibilityHelper.CreateDynamicData(settings.EsiDirectoryPath, extensionFactory, current); }); /* print list of slaves */ var message = new StringBuilder(); var slaves = rootSlaveInfo.Descendants().ToList(); message.AppendLine($"Found {slaves.Count()} slaves:"); slaves.ForEach(current => { message.AppendLine($"{current.DynamicData.Name} (PDOs: {current.DynamicData.PdoSet.Count} - CSA: { current.Csa })"); }); logger.LogInformation(message.ToString().TrimEnd()); /* create variable references for later use */ var variables = slaves.SelectMany(child => child.GetVariableSet()).ToList(); /* create EC Master */ using (var master = new EcMaster(settings, extensionFactory, logger)) { try { master.Configure(rootSlaveInfo); } catch (Exception ex) { logger.LogError(ex.Message); throw; } /* start master */ var random = new Random(); var cts = new CancellationTokenSource(); var task = Task.Run(() => { var sleepTime = 1000 / (int)cycleFrequency; while (!cts.IsCancellationRequested) { master.UpdateIO(DateTime.UtcNow); unsafe { if (variables.Any()) { var myVariableSpan = new Span <int>(variables.First().DataPtr.ToPointer(), 1); myVariableSpan[0] = random.Next(0, 100); } } Thread.Sleep(sleepTime); } }, cts.Token); /* wait for stop signal */ Console.ReadKey(true); cts.Cancel(); await task; } }
public static void CreateDynamicData(string esiDirectoryPath, IExtensionFactory extensionFactory, SlaveInfo slaveInfo) { string name; string description; List <SlavePdo> pdoSet; byte[] base64ImageData; // find ESI if (slaveInfo.Csa != 0) { (slaveInfo.SlaveEsi, slaveInfo.SlaveEsi_Group) = EsiUtilities.FindEsi(esiDirectoryPath, slaveInfo.Manufacturer, slaveInfo.ProductCode, slaveInfo.Revision); } // pdoSet = new List <SlavePdo>(); base64ImageData = new byte[] { }; name = slaveInfo.SlaveEsi.Type.Value; description = slaveInfo.SlaveEsi.Name.FirstOrDefault()?.Value; if (description.StartsWith(name)) { description = description.Substring(name.Length); } else if (string.IsNullOrWhiteSpace(description)) { description = "no description available"; } // PDOs foreach (DataDirection dataDirection in Enum.GetValues(typeof(DataDirection))) { IEnumerable <PdoType> pdoTypeSet = null; switch (dataDirection) { case DataDirection.Output: pdoTypeSet = slaveInfo.SlaveEsi.RxPdo; break; case DataDirection.Input: pdoTypeSet = slaveInfo.SlaveEsi.TxPdo; break; } foreach (PdoType pdoType in pdoTypeSet) { int syncManager; ushort pdoIndex; string pdoName; SlavePdo slavePdo; var osMax = Convert.ToUInt16(pdoType.OSMax); if (osMax == 0) { pdoName = pdoType.Name.First().Value; pdoIndex = (ushort)EsiUtilities.ParseHexDecString(pdoType.Index.Value); syncManager = pdoType.SmSpecified ? pdoType.Sm : -1; slavePdo = new SlavePdo(slaveInfo, pdoName, pdoIndex, osMax, pdoType.Fixed, pdoType.Mandatory, syncManager); pdoSet.Add(slavePdo); IList <SlaveVariable> slaveVariableSet = pdoType.Entry.Select(x => { var variableIndex = (ushort)EsiUtilities.ParseHexDecString(x.Index.Value); var subIndex = Convert.ToByte(x.SubIndex); //// Improve. What about -1 if SubIndex does not exist? return(new SlaveVariable(slavePdo, x.Name?.FirstOrDefault()?.Value, variableIndex, subIndex, dataDirection, EcUtilities.GetOneDasDataTypeFromEthercatDataType(x.DataType?.Value), (byte)x.BitLen)); }).ToList(); slavePdo.SetVariableSet(slaveVariableSet); } else { for (ushort indexOffset = 0; indexOffset <= osMax - 1; indexOffset++) { pdoName = $"{pdoType.Name.First().Value} [{indexOffset}]"; pdoIndex = (ushort)((ushort)EsiUtilities.ParseHexDecString(pdoType.Index.Value) + indexOffset); syncManager = pdoType.SmSpecified ? pdoType.Sm : -1; var indexOffset_Tmp = indexOffset; slavePdo = new SlavePdo(slaveInfo, pdoName, pdoIndex, osMax, pdoType.Fixed, pdoType.Mandatory, syncManager); pdoSet.Add(slavePdo); IList <SlaveVariable> slaveVariableSet = pdoType.Entry.Select(x => { var variableIndex = (ushort)EsiUtilities.ParseHexDecString(x.Index.Value); var subIndex = (byte)(byte.Parse(x.SubIndex) + indexOffset_Tmp); //// Improve. What about -1 if SubIndex does not exist? return(new SlaveVariable(slavePdo, x.Name.FirstOrDefault()?.Value, variableIndex, subIndex, dataDirection, EcUtilities.GetOneDasDataTypeFromEthercatDataType(x.DataType?.Value), (byte)x.BitLen)); }).ToList(); slavePdo.SetVariableSet(slaveVariableSet); } } } } // image data if (slaveInfo.SlaveEsi_Group.ItemElementName == ItemChoiceType1.ImageData16x14) { base64ImageData = (byte[])slaveInfo.SlaveEsi_Group.Item; } if (slaveInfo.SlaveEsi.ItemElementName.ToString() == nameof(ItemChoiceType1.ImageData16x14)) { base64ImageData = (byte[])slaveInfo.SlaveEsi.Item; } // attach dynamic data slaveInfo.DynamicData = new SlaveInfoDynamicData(name, description, pdoSet, base64ImageData); // execute extension logic ExtensibilityHelper.UpdateSlaveExtensions(extensionFactory, slaveInfo); slaveInfo.SlaveExtensionSet.ToList().ForEach(slaveExtension => { slaveExtension.EvaluateSettings(); }); }
static async Task Main(string[] args) { /* Set interface name. Edit this to suit your needs. */ var interfaceName = "eth0"; /* Set ESI location. Make sure it contains ESI files! The default path is /home/{user}/.local/share/ESI */ var localAppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); var esiDirectoryPath = Path.Combine(localAppDataPath, "ESI"); Directory.CreateDirectory(esiDirectoryPath); /* Copy native file. NOT required in end user scenarios, where EtherCAT.NET package is installed via NuGet! */ var codeBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); Directory.EnumerateFiles(Path.Combine(codeBase, "runtimes"), "*soem_wrapper.*", SearchOption.AllDirectories).ToList().ForEach(filePath => { if (filePath.Contains(RuntimeEnvironment.RuntimeArchitecture)) { File.Copy(filePath, Path.Combine(codeBase, Path.GetFileName(filePath)), true); } }); /* create logger */ var loggerFactory = LoggerFactory.Create(loggingBuilder => { loggingBuilder.SetMinimumLevel(LogLevel.Debug); loggingBuilder.AddConsole(); }); var logger = loggerFactory.CreateLogger("EtherCAT Master"); /* create EtherCAT master settings (with 10 Hz cycle frequency) */ var settings = new EcSettings(cycleFrequency: 10U, esiDirectoryPath, interfaceName); /* scan available slaves */ var rootSlave = EcUtilities.ScanDevices(settings.InterfaceName); rootSlave.Descendants().ToList().ForEach(slave => { // If you have special extensions for this slave, add it here: // slave.Extensions.Add(new MyFancyExtension()); /*################ Sample code START ################## * * // Example code to add SDO write request during initialization * // to Beckhoff "EL3021" * if (slave.ProductCode == 0xBCD3052) * { * var dataset = new List<object>(); * dataset.Add((byte)0x01); * * var requests = new List<SdoWriteRequest>() * { * // Index 0x8000 sub index 6: Filter on * new SdoWriteRequest(0x8000, 0x6, dataset) * }; * * slave.Extensions.Add(new InitialSettingsExtension(requests)); * } * ################## Sample code END #################*/ EcUtilities.CreateDynamicData(settings.EsiDirectoryPath, slave); }); /* print list of slaves */ var message = new StringBuilder(); var slaves = rootSlave.Descendants().ToList(); message.AppendLine($"Found {slaves.Count()} slaves:"); foreach (var slave in slaves) { message.AppendLine($"{slave.DynamicData.Name} (PDOs: {slave.DynamicData.Pdos.Count} - CSA: {slave.Csa})"); } logger.LogInformation(message.ToString().TrimEnd()); /* create variable references for later use */ var variables = slaves.SelectMany(child => child.GetVariables()).ToList(); /* create EC Master (short sample) */ using (var master = new EcMaster(settings, logger)) { try { master.Configure(rootSlave); } catch (Exception ex) { logger.LogError(ex.Message); throw; } /* start master */ var random = new Random(); var cts = new CancellationTokenSource(); var task = Task.Run(() => { var sleepTime = 1000 / (int)settings.CycleFrequency; while (!cts.IsCancellationRequested) { master.UpdateIO(DateTime.UtcNow); unsafe { if (variables.Any()) { var myVariableSpan = new Span <int>(variables.First().DataPtr.ToPointer(), 1); myVariableSpan[0] = random.Next(0, 100); } } Thread.Sleep(sleepTime); } }, cts.Token); /* wait for stop signal */ Console.ReadKey(true); cts.Cancel(); await task; } return; /* remove this to run real world sample*/ /* create EC Master (real world sample) */ using (var master = new EcMaster(settings, logger)) { try { master.Configure(rootSlave); } catch (Exception ex) { logger.LogError(ex.Message); throw; } /*################ Sample code START ################## * * // Beckhoff EL2004 (4 channel digital output) * var eL2004 = new DigitalOut(slaves[1]); * * eL2004.SetChannel(1, false); * eL2004.SetChannel(2, true); * eL2004.SetChannel(3, false); * eL2004.SetChannel(4, true); * * // Beckhoff EL1014 (4 channel digital input) * var eL1014 = new DigitalIn(slaves[2]); * * // Beckhoff EL3021 (1 channel analog input - 16bit) * var pdoAnalogIn = slaves[3].DynamicData.Pdos; * var varAnalogIn = pdoAnalogIn[0].Variables.Where(x => x.Name == "Value").First(); * var varUnderrange = pdoAnalogIn[0].Variables.Where(x => x.Name == "Status__Underrange").First(); * var varOverrange = pdoAnalogIn[0].Variables.Where(x => x.Name == "Status__Overrange").First(); * * // Beckhoff EL3021 SDO read (index: 0x8000 sub index: 0x6) * var datasetFilter = new byte[2]; * EcUtilities.SdoRead(master.Context, 4, 0x8000, 6, ref datasetFilter); * var filterOn = BitConverter.ToBoolean(datasetFilter, 0); * logger.LogInformation($"EL3021 filter on: {filterOn}"); * ################## Sample code END #################*/ /* start master */ var random = new Random(); var cts = new CancellationTokenSource(); var task = Task.Run(() => { var sleepTime = 1000 / (int)settings.CycleFrequency; while (!cts.IsCancellationRequested) { master.UpdateIO(DateTime.UtcNow); /*################ Sample code START ################## * * // Beckhoff EL2004 toggle digital output for ch1 and ch3 * eL2004.ToggleChannel(2); * eL2004.ToggleChannel(4); * * // Beckhoff EL1014 read digital input state * logger.LogInformation($"EL1014 channel 1 input: {eL1014.GetChannel(1)}"); * logger.LogInformation($"EL1014 channel 2 input: {eL1014.GetChannel(2)}"); * logger.LogInformation($"EL1014 channel 3 input: {eL1014.GetChannel(3)}"); * logger.LogInformation($"EL1014 channel 4 input: {eL1014.GetChannel(4)}"); * * // Beckhoff EL2004 read digital output state * logger.LogInformation($"EL1014 channel 1 output: {eL2004.GetChannel(1)}"); * logger.LogInformation($"EL1014 channel 2 output: {eL2004.GetChannel(2)}"); * logger.LogInformation($"EL1014 channel 3 output: {eL2004.GetChannel(3)}"); * logger.LogInformation($"EL1014 channel 4 output: {eL2004.GetChannel(4)}"); * * // Beckhoff EL3021 SDO read (index: 0x6000 sub index: 0x2) * // overrange of 12 bit analog input. * var slaveIndex = (ushort)(Convert.ToUInt16(slaves.ToList().IndexOf(slaves[3])) + 1); * var dataset1 = new byte[2]; * EcUtilities.SdoRead(master.Context, slaveIndex, 0x6000, 2, ref dataset1); * bool overrange = BitConverter.ToBoolean(dataset1, 0); * logger.LogInformation($"EL3021 overrange: {overrange}"); * * // Beckhoff EL3021 SDO read (index: 0x6000 sub index: 0x1) * // underrange of 12 bit analog input. * var dataset2 = new byte[2]; * EcUtilities.SdoRead(master.Context, slaveIndex, 0x6000, 1, ref dataset2); * bool underrange = BitConverter.ToBoolean(dataset2, 0); * logger.LogInformation($"EL3021 underrange: {underrange}"); * ################## Sample code END #################*/ unsafe { if (variables.Any()) { /*################ Sample code START ################## * * // Read analog current from EL3021 (16 bit - PDO) * void* data = varAnalogIn.DataPtr.ToPointer(); * int bitmask = (1 << varAnalogIn.BitLength) - 1; * int shift = (*(int*)data >> varAnalogIn.BitOffset) & bitmask; * short analogIn = (short)shift; * logger.LogInformation($"EL3021 analog current in: {analogIn}"); * * // Read analog current underrange status (1 bit - PDO) * void* dataUnder = varUnderrange.DataPtr.ToPointer(); * bitmask = (1 << varUnderrange.BitLength) - 1; * int under = (*(int*)dataUnder >> varUnderrange.BitOffset) & bitmask; * logger.LogInformation($"EL3021 underrange: {under}"); * * // Read analog current overrange status (1 bit - PDO) * void* dataOver = varOverrange.DataPtr.ToPointer(); * bitmask = (1 << varOverrange.BitLength) - 1; * int over = (*(int*)dataOver >> varOverrange.BitOffset) & bitmask; * logger.LogInformation($"EL3021 overrange: {over}"); * ################## Sample code END #################*/ } } Thread.Sleep(sleepTime); } }, cts.Token); /* wait for stop signal */ Console.ReadKey(true); cts.Cancel(); await task; } }
static async Task Main(string[] args) { /* Set interface name. Edit this to suit your needs. */ var interfaceName = "Lokal"; /* Set ESI location. Make sure it contains ESI files! The default path is /home/{user}/.local/share/ESI */ var localAppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); var esiDirectoryPath = Path.Combine(localAppDataPath, "ESI"); Directory.CreateDirectory(esiDirectoryPath); /* Copy native file. NOT required in end user scenarios, where EtherCAT.NET package is installed via NuGet! */ var codeBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); Directory.EnumerateFiles(Path.Combine(codeBase, "runtimes"), "*soem_wrapper.*", SearchOption.AllDirectories).ToList().ForEach(filePath => { if (filePath.Contains(RuntimeEnvironment.RuntimeArchitecture)) { File.Copy(filePath, Path.Combine(codeBase, Path.GetFileName(filePath)), true); } }); /* create logger */ var loggerFactory = LoggerFactory.Create(loggingBuilder => { loggingBuilder.SetMinimumLevel(LogLevel.Debug); loggingBuilder.AddConsole(); }); var logger = loggerFactory.CreateLogger("EtherCAT Master"); /* create EtherCAT master settings (with 10 Hz cycle frequency) */ var settings = new EcSettings(cycleFrequency: 10U, esiDirectoryPath, interfaceName); /* scan available slaves */ var rootSlave = EcUtilities.ScanDevices(settings.InterfaceName); rootSlave.Descendants().ToList().ForEach(slave => { // If you have special extensions for this slave, add it here: // slave.Extensions.Add(new MyFancyExtension()); EcUtilities.CreateDynamicData(settings.EsiDirectoryPath, slave); }); /* print list of slaves */ var message = new StringBuilder(); var slaves = rootSlave.Descendants().ToList(); message.AppendLine($"Found {slaves.Count()} slaves:"); foreach (var slave in slaves) { message.AppendLine($"{slave.DynamicData.Name} (PDOs: {slave.DynamicData.Pdos.Count} - CSA: {slave.Csa})"); } logger.LogInformation(message.ToString().TrimEnd()); /* create variable references for later use */ var variables = slaves.SelectMany(child => child.GetVariables()).ToList(); /* create EC Master */ using (var master = new EcMaster(settings, logger)) { // If you want to change SDO values of a certain slave, register a callback: // // EcHL.RegisterCallback(master.Context, slaveIndex /* 1-based indexing */, slaveIndex => // { // var returnValue = EcUtilities.SdoWrite(master.Context, slaveIndex, ...); // EcUtilities.CheckErrorCode(master.Context, returnValue); // return 0; // }); try { master.Configure(rootSlave); } catch (Exception ex) { logger.LogError(ex.Message); throw; } /* start master */ var random = new Random(); var cts = new CancellationTokenSource(); var task = Task.Run(() => { var sleepTime = 1000 / (int)settings.CycleFrequency; while (!cts.IsCancellationRequested) { master.UpdateIO(DateTime.UtcNow); unsafe { if (variables.Any()) { var myVariableSpan = new Span <int>(variables.First().DataPtr.ToPointer(), 1); myVariableSpan[0] = random.Next(0, 100); } } Thread.Sleep(sleepTime); } }, cts.Token); /* wait for stop signal */ Console.ReadKey(true); cts.Cancel(); await task; } }