Skip to content

Commit

Permalink
Added project template (#1)
Browse files Browse the repository at this point in the history
- Added `MtconnectTranspiler.Sinks.CSharp.Example` project to be used as a starting point for others to create their own transpiler.
  • Loading branch information
tbm0115 authored Mar 9, 2023
1 parent 12c86d1 commit 2a54f0e
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 2 deletions.
Binary file added Mtconnect CSharp Transpiler.zip
Binary file not shown.
13 changes: 13 additions & 0 deletions MtconnectTranspiler.Sinks.CSharp.Example/Models/ExampleClass.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using MtconnectTranspiler.Model;
using MtconnectTranspiler.Sinks.CSharp.Attributes;
using MtconnectTranspiler.Sinks.CSharp.Models;
using MtconnectTranspiler.Xmi.UML;

namespace MtconnectTranspiler.Sinks.CSharp.Example.Models
{
[ScribanTemplate("MtconnectCore.Class.scriban")]
public class ExampleClass : Class
{
public ExampleClass(MTConnectModel model, UmlClass source) : base(model, source) { }
}
}
25 changes: 25 additions & 0 deletions MtconnectTranspiler.Sinks.CSharp.Example/Models/ExampleEnum.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using MtconnectTranspiler.Model;
using MtconnectTranspiler.Sinks.CSharp.Attributes;
using MtconnectTranspiler.Xmi;
using MtconnectTranspiler.Xmi.UML;

namespace MtconnectTranspiler.Sinks.CSharp.Example.Models
{
[ScribanTemplate("Example.Enum.scriban")]
public class ExampleEnum : CSharp.Models.Enum
{
// NOTE: Only used for CATEGORY types that have subTypes.
public Dictionary<string, string> SubTypes { get; set; } = new Dictionary<string, string>();

// NOTE: Only used for CATEGORY types that have value enums.
public Dictionary<string, string> ValueTypes { get; set; } = new Dictionary<string, string>();

public ExampleEnum(MTConnectModel model, XmiElement source, string name) : base(model, source, name) { }

public ExampleEnum(MTConnectModel model, UmlEnumeration source) : base(model, source) { }

public ExampleEnum(MTConnectModel model, UmlPackage source) : base(model, source) { }

public ExampleEnum(MTConnectModel model, MTConnectDeviceInformationModel source) : base(model, source) { }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<StartupObject>Program</StartupObject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Consoul" Version="1.6.2" />
<PackageReference Include="MtconnectTranspiler" Version="0.0.3" />
<PackageReference Include="MtconnectTranspiler.Sinks.CSharp" Version="0.0.12-beta" />
</ItemGroup>

<ItemGroup>
<Content Include="Templates\Example.Class.scriban">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Templates\Example.Enum.scriban">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Templates\UmlCommentsSummaryContent.scriban" />
<Content Update="Templates\UmlCommentsSummaryContent.scriban">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
68 changes: 68 additions & 0 deletions MtconnectTranspiler.Sinks.CSharp.Example/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using ConsoulLibrary;
using Microsoft.Extensions.Logging;
using MtconnectTranspiler;
using MtconnectTranspiler.Sinks.CSharp.Example;

internal class Program
{
private static void Main(string[] args)
{
if (args.Length == 0) throw new ArgumentNullException(nameof(args), "Missing projectPath argument");

string projectPath = args[0];
if (!Directory.Exists(projectPath))
{
Consoul.Write("Creating project path: " + projectPath);
Directory.CreateDirectory(projectPath);
}

var logFactory = LoggerFactory.Create((o) => o.AddConsoulLogger());
var dispatchLogger = logFactory.CreateLogger<TranspilerDispatcher>();
var transpilerLogger = logFactory.CreateLogger<Transpiler>();


// NOTE: The GitHubRelease can be a reference to a specific tag referring to the version in which to download.
TranspilerDispatcherOptions dispatchOptions = null;
if (args.Length > 1)
{
if (!File.Exists(args[1])) throw new FileNotFoundException(args[1]);

dispatchOptions = new FromFileOptions() { Filepath = args[1] };
Consoul.Write("Dispatching from file: " + args[1]);
}
else
{
dispatchOptions = new FromGitHubOptions() { GitHubRelease = "latest" };
Consoul.Write("Dispatching from GitHub's latest release");
}

using (var tokenSource = new CancellationTokenSource())
using (var dispatcher = new TranspilerDispatcher(dispatchOptions, dispatchLogger))
{
dispatcher.AddSink(new Transpiler(projectPath, transpilerLogger));

Consoul.Write("Beginning deserialization and dispatching");
var task = Task.Run(() => dispatcher.TranspileAsync(tokenSource.Token));

#if DEBUG
task = task.ContinueWith((t) => tokenSource.Cancel());
Consoul.Wait(cancellationToken: tokenSource.Token);
#else
task.Wait();
#endif

if (task.IsCompletedSuccessfully)
{
Consoul.Write("Done!", ConsoleColor.Green);

Environment.Exit(0);
}
else
{
Consoul.Write("Cancelled", ConsoleColor.Red);
Environment.Exit(1);
}

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;

namespace {{ to_pascal_code source.namespace }} {
/// <summary>
/// View in the MTConnect Model browser <seealso href="https://model.mtconnect.org/#Structure__{{ source.id }}">model.mtconnect.org</seealso>
{{ source?.summary }}
/// </summary>
[GeneratedCode("MtconnectTranspiler.Sinks.CSharp.Example", "{{ version }}")]
public {{ source.modifier }} class {{ source.name }} {
{{~ for item in source.items ~}}
/// <summary>
{{ item?.summary }}
/// </summary>
/// <remarks>Original Name: {{ item.name }}</remarks>
{{ item.access_modifier }} {{ item.type }} {{ item.name }} { get; set; }
{{~ end ~}}

# region Rules
{{~ for constraint in source.constraints ~}}
/// <summary>
/// {{ constraint.name }}
/// </summary>
/// <remarks>Specification Language: <c>{{ constraint?.specification?.language ?? "Unspecified" }}</c></remarks>
/*
{{ constraint?.raw_script ?? "No Content" }}
*/
{{~ end ~}}
# endregion
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.CodeDom.Compiler;

namespace {{ to_pascal_code source.namespace }}
{
/// <summary>
/// View in the MTConnect Model browser <seealso href="https://model.mtconnect.org/#Enumeration__{{ source.sysml_id }}">model.mtconnect.org</seealso>
{{ include 'UmlCommentsSummaryContent.scriban' source?.summary ~}}
/// </summary>
{{~ if source?.deprecated_version != "" ~}}
[Obsolete("Deprecated according to https://model.mtconnect.org/")]
{{~ end ~}}
[GeneratedCode("MtconnectTranspiler.Sinks.CSharp.Example", "{{ version }}")]
public enum {{ to_pascal_case source.name }}
{
{{~ for item in source.items ~}}
/// <summary>
{{ include 'UmlCommentsSummaryContent.scriban' item?.summary ~}}
/// </summary>
{{~ if item?.deprecated_version != "" ~}}
[Obsolete("Deprecated according to https://model.mtconnect.org/")]
{{~ end ~}}
{{~ if category_contains_type source item ~}}
/// <remarks><b>Observational Sub-Type</b>: {{ source?.sub_types[item.name] }}</remarks>
{{ end ~}}
{{~ if category_contains_value source item ~}}
/// <remarks><b>Observational Value</b>: {{ source?.value_types[item.name] }}</remarks>
{{ end ~}}
{{ item.name }},
{{~ end ~}}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{~ if $1 ~}}
{{ $1 }}
{{~ end ~}}
133 changes: 133 additions & 0 deletions MtconnectTranspiler.Sinks.CSharp.Example/Transpiler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using Microsoft.Extensions.Logging;
using MtconnectTranspiler.Model;
using MtconnectTranspiler.Sinks.CSharp;
using MtconnectTranspiler.Sinks.CSharp.Models;
using MtconnectTranspiler.Sinks.CSharp.Example.Models;
using MtconnectTranspiler.Xmi.UML;
using Scriban.Runtime;

namespace MtconnectTranspiler.Sinks.CSharp.Example
{
public class CategoryFunctions : ScriptObject
{
public static bool CategoryContainsType(ExampleEnum @enum, EnumItem item) => @enum.SubTypes.ContainsKey(item.Name);
public static bool CategoryContainsValue(ExampleEnum @enum, EnumItem item) => @enum.ValueTypes.ContainsKey(item.Name);
public static bool EnumHasValues(ExampleEnum @enum) => @enum.ValueTypes.Any();
}
internal class Transpiler : CsharpTranspiler
{
/// <summary>
///
/// </summary>
/// <param name="projectPath"></param>
public Transpiler(string projectPath, ILogger<Transpiler> logger = default) : base(projectPath, logger) { }

public override void Transpile(MTConnectModel model, CancellationToken cancellationToken = default(CancellationToken))
{
_logger?.LogInformation("Received MTConnectModel, beginning transpilation");

Model.SetValue("model", model, true);

base.TemplateContext.PushGlobal(new CategoryFunctions());

const string DataItemNamespace = "Example.Enums.DataItemTypes";
const string DataItemValueNamespace = "Example.Enums.DataItemValues";

// Process DataItem Types/Sub-Types
var dataItemTypeEnums = new List<ExampleEnum>();
var valueEnums = new List<ExampleEnum>();
string[] categories = new string[] { "Sample", "Event", "Condition" };

foreach (var category in categories)
{
// Get the UmlPackage for the category (ie. Samples, Events, Conditions).
var typesPackage = model
?.ObservationInformationModel
?.ObservationTypes
?.Elements
?.Where(o => o.Name == $"{category} Types")
?.FirstOrDefault() as UmlPackage;
// Get all DataItem Type and SubType references
var allTypes = typesPackage
?.Elements
?.Where(o => o is UmlClass)
?.Select(o => o as UmlClass);
// Filter to get just the Type references
var types = allTypes
?.Where(o => !o.Name.Contains("."));
// Filter and group each SubType by the relevant Type reference
var subTypes = allTypes
?.Where(o => o.Name.Contains("."))
?.GroupBy(o => o.Name.Substring(0, o.Name.IndexOf(".")), o => o)
?.Where(o => o.Any())
?.ToDictionary(o => o.Key, o => o?.ToList());

var categoryEnum = new ExampleEnum(model, typesPackage, $"{category}Types") { Namespace = DataItemNamespace };

foreach (var type in types)
{
// Add type to CATEGORY enum
categoryEnum.AddItem(model, type);

// Find value
var typeResult = type?.Properties?.FirstOrDefault(o => o.Name == "result");
if (typeResult != null)
{
var typeValuesSysEnum = model
?.Profile
?.ProfileDataTypes
?.Elements
?.FirstOrDefault(o => o is UmlEnumeration && o.Id == typeResult.PropertyType);
if (typeValuesSysEnum != null)
{
var typeValuesEnum = new ExampleEnum(model, typeValuesSysEnum as UmlEnumeration) { Namespace = DataItemValueNamespace, Name = $"{type.Name}Values" };
foreach (var value in typeValuesEnum.Items)
{
value.Name = value.SysML_Name;
}
if (!categoryEnum.ValueTypes.ContainsKey(type.Name)) categoryEnum.ValueTypes.Add(ScribanHelperMethods.ToUpperSnakeCode(type.Name), $"{type.Name}Values");
valueEnums.Add(typeValuesEnum);
}
}

// Add subType as enum
if (subTypes.ContainsKey(type.Name))
{
// Register type as having a subType in the CATEGORY enum
if (!categoryEnum.SubTypes.ContainsKey(type.Name)) categoryEnum.SubTypes.Add(ScribanHelperMethods.ToUpperSnakeCode(type.Name), $"{type.Name}SubTypes");

var subTypeEnum = new ExampleEnum(model, type, $"{type.Name}SubTypes") { Namespace = DataItemNamespace };

var typeSubTypes = subTypes[type.Name];
subTypeEnum.AddItems(model, typeSubTypes);

// Cleanup Enum names
foreach (var item in subTypeEnum.Items)
{
if (!item.Name.Contains(".")) continue;
item.Name = ScribanHelperMethods.ToUpperSnakeCode(item.Name.Substring(item.Name.IndexOf(".") + 1));
}

// Register the DataItem SubType Enum
dataItemTypeEnums.Add(subTypeEnum);
}
}

// Cleanup Enum names
foreach (var item in categoryEnum.Items)
{
item.Name = ScribanHelperMethods.ToUpperSnakeCode(item.Name);
}

// Register the DataItem Category Enum (ie. Samples, Events, Conditions)
dataItemTypeEnums.Add(categoryEnum);
}

_logger?.LogInformation($"Processing {dataItemTypeEnums.Count} DataItem types/subTypes");

// Process the template into enum files
processTemplate(dataItemTypeEnums, Path.Combine(ProjectPath, "Enums", "Devices", "DataItemTypes"), true);
processTemplate(valueEnums, Path.Combine(ProjectPath, "Enums", "Streams"), true);
}
}
}
13 changes: 12 additions & 1 deletion MtconnectTranspiler.Sinks.CSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33205.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MtconnectTranspiler.Sinks.CSharp", "MtconnectTranspiler.Sinks.CSharp\MtconnectTranspiler.Sinks.CSharp.csproj", "{E5B69CB5-6763-4D37-9F0D-92B21B60A60A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MtconnectTranspiler.Sinks.CSharp", "MtconnectTranspiler.Sinks.CSharp\MtconnectTranspiler.Sinks.CSharp.csproj", "{E5B69CB5-6763-4D37-9F0D-92B21B60A60A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MtconnectTranspiler.Sinks.CSharp.Example", "MtconnectTranspiler.Sinks.CSharp.Example\MtconnectTranspiler.Sinks.CSharp.Example.csproj", "{297BE937-D617-4F8E-93F3-DFA29C0E31D4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6D9F4E82-1674-498E-987C-5AFC80BA7DBC}"
ProjectSection(SolutionItems) = preProject
Mtconnect CSharp Transpiler.zip = Mtconnect CSharp Transpiler.zip
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -15,6 +22,10 @@ Global
{E5B69CB5-6763-4D37-9F0D-92B21B60A60A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5B69CB5-6763-4D37-9F0D-92B21B60A60A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5B69CB5-6763-4D37-9F0D-92B21B60A60A}.Release|Any CPU.Build.0 = Release|Any CPU
{297BE937-D617-4F8E-93F3-DFA29C0E31D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{297BE937-D617-4F8E-93F3-DFA29C0E31D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{297BE937-D617-4F8E-93F3-DFA29C0E31D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{297BE937-D617-4F8E-93F3-DFA29C0E31D4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Title>MTConnect Transpiler Sink for C#</Title>
<Version>0.0.12-beta</Version>
<Version>0.0.12</Version>
<Authors>mtconnect, tbm0115</Authors>
<Company>MTConnect Institute; TAMS;</Company>
<Description>An implementation of `ITranspilerSink` from the `MtconnectTranspiler` library. This libary makes it possible to transpile the MTConnect Standard SysML model into C# code.</Description>
<RepositoryType>git</RepositoryType>
<PackageTags>MTConnect;Transpiler;CI;C#;</PackageTags>
<PackageProjectUrl>https://github.com/mtconnect/MtconnectTranspiler.Sinks.CSharp</PackageProjectUrl>
<RepositoryUrl>https://github.com/mtconnect/MtconnectTranspiler.Sinks.CSharp</RepositoryUrl>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit 2a54f0e

Please sign in to comment.