Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@
<PackageReference Include="Atc.Analyzer" Version="0.1.22" PrivateAssets="All" />
<PackageReference Include="AsyncFixer" Version="2.1.0" PrivateAssets="All" />
<PackageReference Include="Asyncify" Version="0.9.7" PrivateAssets="All" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.286" PrivateAssets="All" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.302" PrivateAssets="All" />
<PackageReference Include="SecurityCodeScan.VS2019" Version="5.6.7" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507" PrivateAssets="All" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.18.0.131500" PrivateAssets="All" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.19.0.132793" PrivateAssets="All" />
</ItemGroup>

</Project>
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Atc.Kepware is a .NET library and CLI tool for configuring Kepware servers via R

The library provides comprehensive Kepware server management capabilities:

- ✅ **Channel Management**: Create, retrieve, and delete channels for various drivers (EuroMap63, OPC UA Client, Simulator)
- ✅ **Channel Management**: Create, retrieve, and delete channels for 70+ [supported drivers](#supported-drivers)
- ✅ **Device Management**: Configure devices under channels with driver-specific settings
- ✅ **Tag Management**: Create tags and tag groups with hierarchical structures, search for tags with wildcards
- ✅ **IoT Gateway**: Manage MQTT and REST client/server agents and their associated items
Expand Down Expand Up @@ -397,6 +397,14 @@ The library supports a comprehensive range of Kepware drivers organized by manuf
| Wago Ethernet | Wago controllers |
| Yaskawa MP Series Ethernet | MP series motion controllers |

### Flow Computers / Metering
| Driver | Description |
|--------|-------------|
| ABB Totalflow | ABB Totalflow flow computers |
| Fisher ROC Ethernet | Fisher ROC flow computers over Ethernet |
| Fisher ROC Plus Ethernet | Fisher ROC Plus flow computers over Ethernet |
| Omni Flow Computer | Omni Flow Computer metering |

### CNC / Motion
| Driver | Description |
|--------|-------------|
Expand Down Expand Up @@ -444,11 +452,11 @@ dotnet tool update --global atc-kepware-configuration

## Commands

The CLI is organized into two main command groups:
The CLI is organized into two main command groups. The connectivity group includes subcommands for channels, devices, tags, and meters:

### Connectivity Commands

Manage channels, devices, and tags:
Manage channels, devices, tags, and meters:

```bash
# List all channels
Expand Down Expand Up @@ -477,6 +485,19 @@ atc-kepware-configuration connectivity tags create tag -s <server-url> \
--address R0001 \
--data-type Word \
--scan-rate 1000

# Get meters for a flow computer meter group
atc-kepware-configuration connectivity meters get abbtotalflow -s <server-url> \
--channel-name MyChannel \
--device-name MyDevice \
--meter-group-name MyMeterGroup

# Create a meter
atc-kepware-configuration connectivity meters create fisherrocethernet -s <server-url> \
--channel-name MyChannel \
--device-name MyDevice \
--meter-group-name MyMeterGroup \
--name MyMeter
```

### IoT Gateway Commands
Expand Down
4 changes: 2 additions & 2 deletions sample/Atc.Kepware.Sample/Atc.Kepware.Sample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.3" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Atc" Version="3.0.16" />
<PackageReference Include="Atc.Console.Spectre" Version="3.0.16" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
<PackageReference Include="Atc" Version="3.0.18" />
<PackageReference Include="Atc.Console.Spectre" Version="3.0.18" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.3" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace Atc.Kepware.Configuration.CLI.Commands.Connectivity.Meters.Create;

public sealed class MeterCreateAbbTotalflowCommand : AsyncCommand<MeterCreateCommandBaseSettings>
{
private readonly ILogger<MeterCreateAbbTotalflowCommand> logger;
private readonly IKepwareConfigurationClient kepwareConfigurationClient;

public MeterCreateAbbTotalflowCommand(
ILoggerFactory loggerFactory,
IKepwareConfigurationClient kepwareConfigurationClient)
{
logger = loggerFactory.CreateLogger<MeterCreateAbbTotalflowCommand>();
this.kepwareConfigurationClient = kepwareConfigurationClient;
}

public override Task<int> ExecuteAsync(
CommandContext context,
MeterCreateCommandBaseSettings settings,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(settings);

return ExecuteInternalAsync(settings, cancellationToken);
}

private async Task<int> ExecuteInternalAsync(
MeterCreateCommandBaseSettings settings,
CancellationToken cancellationToken)
{
ConsoleHelper.WriteHeader();

try
{
kepwareConfigurationClient.SetConnectionInformation(
new Uri(settings.ServerUrl),
settings.UserName!.Value,
settings.Password!.Value);

var request = BuildAbbTotalflowMeterRequest(settings);
var result = await kepwareConfigurationClient.CreateAbbTotalflowMeter(
request,
settings.ChannelName,
settings.DeviceName,
settings.MeterGroupName,
cancellationToken);

if (!result.CommunicationSucceeded ||
result.StatusCode is not (HttpStatusCode.OK or HttpStatusCode.Created))
{
return ConsoleExitStatusCodes.Failure;
}
}
catch (Exception ex)
{
logger.LogError($"{EmojisConstants.Error} {ex.GetMessage()}");
return ConsoleExitStatusCodes.Failure;
}

logger.LogInformation($"{EmojisConstants.Success} Done");
return ConsoleExitStatusCodes.Success;
}

private static AbbTotalflowMeterRequest BuildAbbTotalflowMeterRequest(
MeterCreateCommandBaseSettings settings)
=> new()
{
Name = settings.Name,
Description = settings.Description is not null && settings.Description.IsSet
? settings.Description.Value
: string.Empty,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace Atc.Kepware.Configuration.CLI.Commands.Connectivity.Meters.Create;

public sealed class MeterCreateFisherRocEthernetCommand : AsyncCommand<MeterCreateCommandBaseSettings>
{
private readonly ILogger<MeterCreateFisherRocEthernetCommand> logger;
private readonly IKepwareConfigurationClient kepwareConfigurationClient;

public MeterCreateFisherRocEthernetCommand(
ILoggerFactory loggerFactory,
IKepwareConfigurationClient kepwareConfigurationClient)
{
logger = loggerFactory.CreateLogger<MeterCreateFisherRocEthernetCommand>();
this.kepwareConfigurationClient = kepwareConfigurationClient;
}

public override Task<int> ExecuteAsync(
CommandContext context,
MeterCreateCommandBaseSettings settings,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(settings);

return ExecuteInternalAsync(settings, cancellationToken);
}

private async Task<int> ExecuteInternalAsync(
MeterCreateCommandBaseSettings settings,
CancellationToken cancellationToken)
{
ConsoleHelper.WriteHeader();

try
{
kepwareConfigurationClient.SetConnectionInformation(
new Uri(settings.ServerUrl),
settings.UserName!.Value,
settings.Password!.Value);

var request = BuildFisherRocEthernetMeterRequest(settings);
var result = await kepwareConfigurationClient.CreateFisherRocEthernetMeter(
request,
settings.ChannelName,
settings.DeviceName,
settings.MeterGroupName,
cancellationToken);

if (!result.CommunicationSucceeded ||
result.StatusCode is not (HttpStatusCode.OK or HttpStatusCode.Created))
{
return ConsoleExitStatusCodes.Failure;
}
}
catch (Exception ex)
{
logger.LogError($"{EmojisConstants.Error} {ex.GetMessage()}");
return ConsoleExitStatusCodes.Failure;
}

logger.LogInformation($"{EmojisConstants.Success} Done");
return ConsoleExitStatusCodes.Success;
}

private static FisherRocEthernetMeterRequest BuildFisherRocEthernetMeterRequest(
MeterCreateCommandBaseSettings settings)
=> new()
{
Name = settings.Name,
Description = settings.Description is not null && settings.Description.IsSet
? settings.Description.Value
: string.Empty,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace Atc.Kepware.Configuration.CLI.Commands.Connectivity.Meters.Create;

public sealed class MeterCreateFisherRocPlusEthernetCommand : AsyncCommand<MeterCreateCommandBaseSettings>
{
private readonly ILogger<MeterCreateFisherRocPlusEthernetCommand> logger;
private readonly IKepwareConfigurationClient kepwareConfigurationClient;

public MeterCreateFisherRocPlusEthernetCommand(
ILoggerFactory loggerFactory,
IKepwareConfigurationClient kepwareConfigurationClient)
{
logger = loggerFactory.CreateLogger<MeterCreateFisherRocPlusEthernetCommand>();
this.kepwareConfigurationClient = kepwareConfigurationClient;
}

public override Task<int> ExecuteAsync(
CommandContext context,
MeterCreateCommandBaseSettings settings,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(settings);

return ExecuteInternalAsync(settings, cancellationToken);
}

private async Task<int> ExecuteInternalAsync(
MeterCreateCommandBaseSettings settings,
CancellationToken cancellationToken)
{
ConsoleHelper.WriteHeader();

try
{
kepwareConfigurationClient.SetConnectionInformation(
new Uri(settings.ServerUrl),
settings.UserName!.Value,
settings.Password!.Value);

var request = BuildFisherRocPlusEthernetMeterRequest(settings);
var result = await kepwareConfigurationClient.CreateFisherRocPlusEthernetMeter(
request,
settings.ChannelName,
settings.DeviceName,
settings.MeterGroupName,
cancellationToken);

if (!result.CommunicationSucceeded ||
result.StatusCode is not (HttpStatusCode.OK or HttpStatusCode.Created))
{
return ConsoleExitStatusCodes.Failure;
}
}
catch (Exception ex)
{
logger.LogError($"{EmojisConstants.Error} {ex.GetMessage()}");
return ConsoleExitStatusCodes.Failure;
}

logger.LogInformation($"{EmojisConstants.Success} Done");
return ConsoleExitStatusCodes.Success;
}

private static FisherRocPlusEthernetMeterRequest BuildFisherRocPlusEthernetMeterRequest(
MeterCreateCommandBaseSettings settings)
=> new()
{
Name = settings.Name,
Description = settings.Description is not null && settings.Description.IsSet
? settings.Description.Value
: string.Empty,
};
}
Loading