Skip to content

Commit

Permalink
Fix #47 and #49. (#50)
Browse files Browse the repository at this point in the history
* Improve DC init error message.

* Properly parse HexDec strings.

* Improve code style.

* Fix missing subindex padding bug.

* Prepare release.
  • Loading branch information
Apollo3zehn committed May 28, 2020
1 parent 5ea922a commit ad44c4f
Show file tree
Hide file tree
Showing 12 changed files with 122 additions and 134 deletions.
2 changes: 1 addition & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ deploy:
- provider: NuGet
server: https://www.nuget.org/api/v2/package
api_key:
secure: WqZcnRwOmzIa+INti/HnihBrUz0u71rTRNElX/2wbuAIbzMX6SV8h4OxJtCaX2vN
secure: hyUV3r8p7ThyhI+6EqjtLLvxprX0EJpbUcaj8XUNTvT9Yb9/OW2pnE1Ue7wiATwu
skip_symbols: true
artifact: /.*\.nupkg/
on:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# EtherCAT.NET

[![AppVeyor](https://ci.appveyor.com/api/projects/status/github/apollo3zehn/ethercat.net?svg=true&branch=master)](https://ci.appveyor.com/project/Apollo3zehn/ethercat-net)
[![NuGet](https://img.shields.io/nuget/vpre/ethercat.net.svg)](https://www.nuget.org/packages/EtherCAT.NET)

A large amount of the logic of EtherCAT.NET comes from the data acquisition software [OneDAS](https://github.com/OneDAS-Group/OneDAS-Core), where the master has been extensively tested on many slaves from Beckhoff, Gantner and Anybus. Due to the effort to reduce protocol specific logic within OneDAS and to allow standalone use of the EtherCAT master, EtherCAT.NET was born.

Expand Down
2 changes: 1 addition & 1 deletion build/build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<Major Condition="$(Major) == ''">1</Major>
<Minor Condition="$(Minor) == ''">0</Minor>
<Revision Condition="$(Revision) == ''">0</Revision>
<VersionSuffix Condition="$(VersionSuffix) == ''">alpha.3</VersionSuffix>
<VersionSuffix Condition="$(VersionSuffix) == ''">alpha.4</VersionSuffix>
</PropertyGroup>

<PropertyGroup>
Expand Down
20 changes: 19 additions & 1 deletion native/SOEM_wrapper/soem_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,25 @@ int CALLCONV SdoWrite(ecx_contextt* context, uint16 slaveIndex, uint16 sdoIndex,
totalByteCount += byteCountSet[i];
}

returnValue += ecx_SDOwrite(context, slaveIndex, sdoIndex, sdoSubIndex, TRUE, totalByteCount, dataset, timeout);
// Clause 12.2 of the ETG.1020 specification, sub-point 1 "SDO Complete Access" -> Subindex 0 is padded to 16 bits
if (sdoSubIndex == 0)
{
uint8* datasetPadded = calloc(totalByteCount + 1, 1);

datasetPadded[0] = dataset[0];

for (int i = 1; i < totalByteCount; i++)
{
datasetPadded[i + 1] = dataset[i];
}

returnValue += ecx_SDOwrite(context, slaveIndex, sdoIndex, sdoSubIndex, TRUE, totalByteCount + 1, datasetPadded, timeout);
free(datasetPadded);
}
else
{
returnValue += ecx_SDOwrite(context, slaveIndex, sdoIndex, sdoSubIndex, TRUE, totalByteCount, dataset, timeout);
}

return returnValue == 1 ? 1 : 0;
}
Expand Down
4 changes: 0 additions & 4 deletions src/EtherCAT.NET/EcUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,9 @@ static EcUtilities()
public static OneDasDataType GetOneDasDataTypeFromEthercatDataType(string value)
{
if (value == null)
{
return 0;
}
else
{
return EcUtilities.GetOneDasDataTypeFromEthercatDataType((EthercatDataType)Enum.Parse(typeof(EthercatDataType), value));
}
}

public static OneDasDataType GetOneDasDataTypeFromEthercatDataType(EthercatDataType ethercatDataType)
Expand Down
148 changes: 63 additions & 85 deletions src/EtherCAT.NET/EsiUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,9 @@ static EsiUtilities()

private static EtherCATInfo LoadEsi(string esiFileName)
{
XmlSerializer xmlSerializer;
EtherCATInfo etherCatInfo;

xmlSerializer = new XmlSerializer(typeof(EtherCATInfo));
var xmlSerializer = new XmlSerializer(typeof(EtherCATInfo));

using (StreamReader streamReader = new StreamReader(esiFileName))
{
Expand Down Expand Up @@ -156,36 +155,26 @@ private static (EtherCATInfo etherCATInfo, EtherCATInfoDescriptionsDevice device

foreach (var currentInfo in etherCATInfoSet)
{
uint vendorId;

vendorId = uint.Parse(currentInfo.Vendor.Id.Replace("#x", string.Empty), NumberStyles.HexNumber);
var vendorId = (uint)EsiUtilities.ParseHexDecString(currentInfo.Vendor.Id);

if (vendorId != manufacturer)
{
continue;
}

device = currentInfo.Descriptions.Devices.FirstOrDefault(currentDevice =>
{
bool found;
found = !string.IsNullOrWhiteSpace(currentDevice.Type.ProductCode) &&
var found = !string.IsNullOrWhiteSpace(currentDevice.Type.ProductCode) &&
!string.IsNullOrWhiteSpace(currentDevice.Type.RevisionNo) &&
Int32.Parse(currentDevice.Type.ProductCode.Substring(2), NumberStyles.HexNumber) == productCode &&
Int32.Parse(currentDevice.Type.RevisionNo.Substring(2), NumberStyles.HexNumber) == revision;
(int)EsiUtilities.ParseHexDecString(currentDevice.Type.ProductCode) == productCode &&
(int)EsiUtilities.ParseHexDecString(currentDevice.Type.RevisionNo) == revision;
if (found)
{
info = currentInfo;
}
return found;
});

if (device != null)
{
break;
}
}

// try to find old revision
Expand All @@ -195,76 +184,109 @@ private static (EtherCATInfo etherCATInfo, EtherCATInfoDescriptionsDevice device
{
device = currentInfo.Descriptions.Devices.Where(currentDevice =>
{
bool found;
found = !string.IsNullOrWhiteSpace(currentDevice.Type.ProductCode) &&
!string.IsNullOrWhiteSpace(currentDevice.Type.RevisionNo) &&
Int32.Parse(currentDevice.Type.ProductCode.Substring(2), NumberStyles.HexNumber) == productCode;
var found = !string.IsNullOrWhiteSpace(currentDevice.Type.ProductCode) &&
!string.IsNullOrWhiteSpace(currentDevice.Type.RevisionNo) &&
(int)EsiUtilities.ParseHexDecString(currentDevice.Type.ProductCode) == productCode;
if (found)
{
info = currentInfo;
}
return found;
}).OrderBy(currentDevice => currentDevice.Type.RevisionNo).LastOrDefault();
});

// return without success
if (device == null)
{
return (null, null, null);
}
}

// find group
group = info.Descriptions.Groups.FirstOrDefault(currentGroup => currentGroup.Type == device.GroupType);

if (group == null)
{
throw new Exception($"ESI entry for group type '{device}' not found.");
}

return (info, device, group);
}

private static bool UpdateCache(string esiSourceDirectoryPath, uint manufacturer, uint productCode, uint revision)
public static void ResetCache()
{
lock (_lock)
{
foreach (var file in new DirectoryInfo(_cacheDirectoryPath).GetFiles())
{
file.Delete();
}

EsiUtilities.CacheEtherCatInfoSet = new List<EtherCATInfo>();
EsiUtilities.SourceEtherCatInfoSet = new List<EtherCATInfo>();
}
}

public static (EtherCATInfoDescriptionsDevice device, EtherCATInfoDescriptionsGroup group) FindEsi(string esiSourceDirectoryPath, uint manufacturer, uint productCode, uint revision)
{
// try to find ESI in cache
(_, var device, var group) = EsiUtilities.TryFindDevice(EsiUtilities.CacheEtherCatInfoSet, manufacturer, productCode, revision);

if (device == null)
{
// update cache
EsiUtilities.UpdateCache(esiSourceDirectoryPath, manufacturer, productCode, revision);

// try to find ESI in cache again
(_, device, group) = EsiUtilities.TryFindDevice(EsiUtilities.CacheEtherCatInfoSet, manufacturer, productCode, revision);

// it finally failed
if (device == null)
{
throw new Exception($"Could not find ESI information of manufacturer '0x{manufacturer:X}' for slave with product code '0x{productCode:X}' and revision '0x{revision:X}'.");
}
}

return (device, group);
}

public static List<DistributedClocksOpMode> GetOpModes(this SlaveInfo slaveInfo)
{
EtherCATInfo cacheInfo;
EtherCATInfo sourceInfo;
EtherCATInfoDescriptionsDevice sourceDevice;
List<EtherCATInfoDescriptionsGroup> cacheGroupSet;
return slaveInfo.SlaveEsi.Dc.OpMode.Select(opMode => new DistributedClocksOpMode(opMode)).ToList();
}

public static long ParseHexDecString(string value)
{
if (value.StartsWith("#x"))
return uint.Parse(value.Replace("#x", string.Empty), NumberStyles.HexNumber);
else
return long.Parse(value);
}

private static bool UpdateCache(string esiSourceDirectoryPath, uint manufacturer, uint productCode, uint revision)
{
// check if source ESI files have been loaded
if (!EsiUtilities.SourceEtherCatInfoSet.Any())
{
EsiUtilities.LoadEsiSource(esiSourceDirectoryPath);
}

// try to find requested device info
(sourceInfo, sourceDevice, _) = EsiUtilities.TryFindDevice(EsiUtilities.SourceEtherCatInfoSet, manufacturer, productCode, revision);
(var sourceInfo, var sourceDevice, _) = EsiUtilities.TryFindDevice(EsiUtilities.SourceEtherCatInfoSet, manufacturer, productCode, revision);

if (sourceDevice == null)
{
return false;
}

lock (_lock)
{
// find matching EtherCATInfo in cache
cacheInfo = EsiUtilities.CacheEtherCatInfoSet.FirstOrDefault(current =>
var cacheInfo = EsiUtilities.CacheEtherCatInfoSet.FirstOrDefault(current =>
{
uint vendorId;
vendorId = current.Vendor.Id.Length > 2 ? uint.Parse(current.Vendor.Id.Substring(2), NumberStyles.HexNumber) : uint.Parse(current.Vendor.Id, NumberStyles.HexNumber);
var vendorId = (uint)EsiUtilities.ParseHexDecString(current.Vendor.Id);
return vendorId == manufacturer;
});

// extend cache file
if (cacheInfo != null)
{
// add new groups
cacheGroupSet = cacheInfo.Descriptions.Groups.ToList();
var cacheGroupSet = cacheInfo.Descriptions.Groups.ToList();

foreach (var sourceGroup in sourceInfo.Descriptions.Groups)
{
Expand Down Expand Up @@ -296,56 +318,12 @@ private static bool UpdateCache(string esiSourceDirectoryPath, uint manufacturer
return true;
}

public static void ResetCache()
{
lock (_lock)
{
foreach (var file in new DirectoryInfo(_cacheDirectoryPath).GetFiles())
{
file.Delete();
}

EsiUtilities.CacheEtherCatInfoSet = new List<EtherCATInfo>();
EsiUtilities.SourceEtherCatInfoSet = new List<EtherCATInfo>();
}
}

public static (EtherCATInfoDescriptionsDevice device, EtherCATInfoDescriptionsGroup group) FindEsi(string esiSourceDirectoryPath, uint manufacturer, uint productCode, uint revision)
{
EtherCATInfoDescriptionsDevice device;
EtherCATInfoDescriptionsGroup group;

// try to find ESI in cache
(_, device, group) = EsiUtilities.TryFindDevice(EsiUtilities.CacheEtherCatInfoSet, manufacturer, productCode, revision);

if (device == null)
{
// update cache
EsiUtilities.UpdateCache(esiSourceDirectoryPath, manufacturer, productCode, revision);

// try to find ESI in cache again
(_, device, group) = EsiUtilities.TryFindDevice(EsiUtilities.CacheEtherCatInfoSet, manufacturer, productCode, revision);

// it finally failed
if (device == null)
{
throw new Exception($"Could not find ESI information of manufacturer '0x{manufacturer:X}' for slave with product code '0x{productCode:X}' and revision '0x{revision:X}'.");
}
}

return (device, group);
}

public static List<DistributedClocksOpMode> GetOpModes(this SlaveInfo slaveInfo)
{
return slaveInfo.SlaveEsi.Dc.OpMode.Select(opMode => new DistributedClocksOpMode(opMode)).ToList();
}

#endregion

#region Properties

private static List<EtherCATInfo> CacheEtherCatInfoSet { get; set; }

private static List<EtherCATInfo> SourceEtherCatInfoSet { get; set; }

#endregion
Expand Down
21 changes: 8 additions & 13 deletions src/EtherCAT.NET/Extensibility/ExtensibilityHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,16 @@ public static void CreateDynamicData(string esiDirectoryPath, IExtensionFactory
foreach (PdoType pdoType in pdoTypeSet)
{
int syncManager;

ushort osMax;
ushort pdoIndex;
ushort indexOffset_Tmp;

string pdoName;

SlavePdo slavePdo;

osMax = Convert.ToUInt16(pdoType.OSMax);
var osMax = Convert.ToUInt16(pdoType.OSMax);

if (osMax == 0)
{
pdoName = pdoType.Name.First().Value;
pdoIndex = ushort.Parse(pdoType.Index.Value.Substring(2), NumberStyles.HexNumber);
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);
Expand All @@ -84,8 +79,8 @@ public static void CreateDynamicData(string esiDirectoryPath, IExtensionFactory

IList<SlaveVariable> slaveVariableSet = pdoType.Entry.Select(x =>
{
ushort variableIndex = ushort.Parse(x.Index.Value.Substring(2), NumberStyles.HexNumber);
byte subIndex = Convert.ToByte(Convert.ToByte(x.SubIndex));
var variableIndex = (ushort)EsiUtilities.ParseHexDecString(x.Index.Value);
var subIndex = byte.Parse(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();
Expand All @@ -97,18 +92,18 @@ public static void CreateDynamicData(string esiDirectoryPath, IExtensionFactory
for (ushort indexOffset = 0; indexOffset <= osMax - 1; indexOffset++)
{
pdoName = $"{pdoType.Name.First().Value} [{indexOffset}]";
pdoIndex = (ushort)(ushort.Parse(pdoType.Index.Value.Substring(2), NumberStyles.HexNumber) + indexOffset);
pdoIndex = (ushort)((ushort)EsiUtilities.ParseHexDecString(pdoType.Index.Value) + indexOffset);
syncManager = pdoType.SmSpecified ? pdoType.Sm : -1;
indexOffset_Tmp = indexOffset;
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 =>
{
ushort variableIndex = ushort.Parse(x.Index.Value.Substring(2), NumberStyles.HexNumber);
byte subIndex = Convert.ToByte(Convert.ToByte(x.SubIndex) + indexOffset_Tmp);
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();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using EtherCAT.NET.Infrastructure;
using System;
using System.Globalization;
using System.Runtime.Serialization;

namespace EtherCAT.NET.Extension
Expand All @@ -10,9 +9,7 @@ public class DistributedClocksOpMode
{
public DistributedClocksOpMode(DeviceTypeDCOpMode opMode)
{
int assignActivate;

assignActivate = Int32.Parse(opMode.AssignActivate.Substring(2), NumberStyles.HexNumber);
var assignActivate = EsiUtilities.ParseHexDecString(opMode.AssignActivate);

this.Name = opMode.Name;
this.Description = opMode.Desc;
Expand Down
Loading

0 comments on commit ad44c4f

Please sign in to comment.