blob: 12aee9bffbcf337a66d54881c2f98206dcd2e7bf [file]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
using System.Collections.Generic;
using org.apache.plc4net.tools.codegen.model.terms;
namespace org.apache.plc4net.tools.codegen.model.fields
{
/// <summary>Base for the mspec field keywords.</summary>
public abstract class Field
{
public required string Name { get; set; }
/// <summary>The field's declared type. Null for <c>typeSwitch</c>.</summary>
public TypeReference? Type { get; set; }
public Dictionary<string, Term> Attributes { get; } = new Dictionary<string, Term>();
/// <summary>Whether the field holds a value the message carries (a
/// property + constructor parameter). False for the fields that exist
/// only for the wire format.</summary>
public virtual bool IsProperty => false;
/// <summary>The mspec keyword, for diagnostics and switch templates.</summary>
public string Keyword => GetType().Name.Replace("Field", "").ToLowerInvariant();
}
/// <summary><c>[simple uint 16 startingAddress]</c></summary>
public sealed class SimpleField : Field
{
public override bool IsProperty => true;
}
/// <summary><c>[const uint 16 protocolIdentifier 0x0000]</c> - written on
/// serialize, checked on parse, exposed as a C# <c>const</c>.</summary>
public sealed class ConstField : Field
{
public required Term ReferenceValue { get; set; }
}
/// <summary><c>[implicit uint 16 length 'pdu.lengthInBytes + 1']</c> -
/// computed on serialize, read-and-kept-as-a-local on parse.</summary>
public sealed class ImplicitField : Field
{
public required Term SerializeExpression { get; set; }
}
/// <summary><c>[reserved uint 15 '0x0000']</c> - no name, no property.</summary>
public sealed class ReservedField : Field
{
public required Term ReferenceValue { get; set; }
}
/// <summary><c>[virtual uint 16 foo 'a + b']</c> - never on the wire, a
/// computed accessor.</summary>
public sealed class VirtualField : Field
{
public required Term ValueExpression { get; set; }
public override bool IsProperty => false;
}
/// <summary>
/// <c>[discriminator uint 7 functionFlag]</c> - read first on the parent so
/// the type switch can pick the child. Exposed as an (abstract on the
/// parent, override on each child) accessor, not a stored property.
/// </summary>
public sealed class DiscriminatorField : Field
{
}
/// <summary><c>[enum TransportSize transportSize code]</c> - an enum-typed
/// field whose wire value is keyed on one of the enum's attributes rather
/// than the enum constant itself. <see cref="Field.Type"/> is an
/// <see cref="EnumTypeReference"/>.</summary>
public sealed class EnumField : Field
{
/// <summary>The enum attribute the wire value maps through - <c>code</c>
/// in <c>[enum TransportSize transportSize code]</c>. Null for the bare
/// two-token form, where the wire value is the enum constant itself.</summary>
public string? KeyAccessor { get; set; }
public override bool IsProperty => true;
}
/// <summary><c>[array byte value count 'byteCount']</c></summary>
public sealed class ArrayField : Field
{
public enum Loop { Count, Length, Terminated }
public Loop LoopType { get; set; }
/// <summary>The count / byte-length / terminator expression.</summary>
public required Term LoopExpression { get; set; }
public override bool IsProperty => true;
}
/// <summary><c>[checksum uint 16 crc 'STATIC_CALL("rtuCrcCheck", ...)']</c></summary>
public sealed class ChecksumField : Field
{
public required Term ChecksumExpression { get; set; }
}
/// <summary>
/// <c>[typeSwitch driverType, response ['MODBUS_TCP' ModbusTcpADU [...]]]</c>
/// - the discriminated dispatch. The case sub-types are lifted to top-level
/// <see cref="ComplexTypeDefinition"/>s by the builder; the names are kept
/// here in declaration order for the parse/serialize switch.
/// </summary>
public sealed class TypeSwitchField : Field
{
/// <summary>The discriminator expressions to switch on, e.g.
/// <c>errorFlag</c>, <c>functionFlag</c>, <c>response</c>.</summary>
public IReadOnlyList<Term> Discriminators { get; set; } = System.Array.Empty<Term>();
/// <summary>Child type names in declaration order.</summary>
public List<string> CaseNames { get; } = new List<string>();
}
/// <summary>
/// <c>[optional uint 16 errorCode 'condition']</c> - present on the wire
/// only when the condition holds. The property is nullable.
/// </summary>
public sealed class OptionalField : Field
{
/// <summary>The presence condition, or null (always present - rare).</summary>
public Term? Condition { get; set; }
public override bool IsProperty => true;
}
/// <summary>
/// <c>[manual Type name 'parse' 'serialize' 'length']</c> - the codec is
/// three hand-supplied expressions (usually <c>STATIC_CALL</c>s that take
/// <c>readBuffer</c> / <c>writeBuffer</c> / <c>_value</c>).
/// </summary>
public sealed class ManualField : Field
{
public required Term ParseExpression { get; set; }
public required Term SerializeExpression { get; set; }
public required Term LengthExpression { get; set; }
public override bool IsProperty => true;
}
/// <summary>
/// <c>[padding uint 8 pad '0x00' 'timesPadding']</c> - writes the padding
/// value <c>timesPadding</c> times; on parse, reads and drops that many.
/// </summary>
public sealed class PaddingField : Field
{
public required Term PaddingValue { get; set; }
public required Term TimesPadding { get; set; }
}
/// <summary>
/// A field keyword the generator does not handle yet
/// (<c>abstract</c>, <c>assert</c>, <c>manualArray</c>, <c>peek</c>,
/// <c>unknown</c>, <c>validation</c>). Kept in the model so a type that
/// uses one still round-trips through the builder and the gap is visible
/// in the output.
/// </summary>
public sealed class UnsupportedField : Field
{
public required string MspecKeyword { get; set; }
public required string RawText { get; set; }
}
}