Innovenergy_trunk/csharp/Lib/Protocols/DBus/Protocol/Message.cs

221 lines
7.0 KiB
C#

using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.Protocols.DBus.Protocol.DataTypes;
using InnovEnergy.Lib.Protocols.DBus.Protocol.DataTypes.Convert;
using InnovEnergy.Lib.Protocols.DBus.Protocol.DataTypes.Signatures;
using InnovEnergy.Lib.Protocols.DBus.Protocol.Header;
using InnovEnergy.Lib.Protocols.DBus.Transport;
using InnovEnergy.Lib.Protocols.DBus.WireFormat;
namespace InnovEnergy.Lib.Protocols.DBus.Protocol;
using HeaderType = ValueTuple<Byte, Byte, Byte, Byte, UInt32, UInt32, IReadOnlyList<(Byte, Variant)>>;
using HeaderFieldType = ValueTuple<Byte, Variant>;
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public readonly struct Message
{
public const Byte ProtocolVersion = 1;
private static Signature HeaderSignature { get; } = Signature.FromType<HeaderType>();
private HeaderType Header { get; }
public Object? Payload { get; }
public Boolean ParsingError { get; }
public MessageType Type => Header.MessageType();
public UInt32 Serial => Header.Serial();
public Signature? Signature => Header.Signature();
public String? Interface => Header.Interface();
public String? Member => Header.Member();
public String? Destination => Header.Destination();
public String? Sender => Header.Sender();
public String? ErrorName => Header.ErrorName();
public UInt32? ReplySerial => Header.ReplySerial();
public UInt32? PayloadLength => Header.PayloadLength();
public ObjectPath? ObjectPath => Header.ObjectPath();
public Endian Endian => Header.Endian();
public Boolean ReplyExpected => Header.ReplyExpected();
public Boolean AutoStart => Header.AutoStart();
public Boolean AllowInteractiveAuthorization => Header.AllowInteractiveAuthorization();
public Boolean HasPayload => Payload is not null;
public Boolean IsError => Type == MessageType.Error;
public Message(HeaderType header, Object? payload) : this(header, payload, false)
{
}
private Message(HeaderType header, Object? payload, Boolean parsingError)
{
Header = header;
Payload = payload;
ParsingError = parsingError;
CheckProtocolVersion();
}
private void CheckProtocolVersion()
{
if (Header.ProtocolVersion() != ProtocolVersion)
throw new Exception($"Unsupported protocol version: expecting {ProtocolVersion}, got {Header.ProtocolVersion()}");
}
public static Message Signal(ObjectPath? objectPath,
String? member,
String? @interface = null,
Object? payload = null)
{
var signature = payload.GetSignature().AsRootSignature();
var fields = new List<HeaderFieldType>();
AddField(FieldCode.Path, objectPath);
AddField(FieldCode.Interface, @interface);
AddField(FieldCode.Member, member);
if (!signature.IsEmpty)
AddField(FieldCode.Signature, signature);
var bodyLength = (UInt32)signature.MeasureSize(payload);
var serial = SerialSource.Next();
HeaderType header =
(
(Byte)Env.Endianness,
(Byte)MessageType.Signal,
(Byte)HeaderFlags.NoReplyExpected,
ProtocolVersion,
bodyLength,
serial,
fields
);
return new Message(header, payload);
void AddField(FieldCode fieldCode, Object? value)
{
if (value is not null)
fields.Add(((Byte)fieldCode, value.Variant()));
}
}
public static Message MethodCall(String? destination,
ObjectPath? objectPath,
String? member,
String? @interface = null,
Object? payload = null,
Boolean replyExpected = true)
{
var signature = payload.GetSignature().AsRootSignature();
var fields = new List<HeaderFieldType>();
AddField(FieldCode.Path, objectPath);
AddField(FieldCode.Destination, destination);
AddField(FieldCode.Interface, @interface);
AddField(FieldCode.Member, member);
if (!signature.IsEmpty)
AddField(FieldCode.Signature, signature);
var flags = replyExpected ? HeaderFlags.None : HeaderFlags.NoReplyExpected;
var bodyLength = (UInt32)signature.MeasureSize(payload);
var serial = SerialSource.Next();
HeaderType header =
(
(Byte)Env.Endianness,
(Byte)MessageType.MethodCall,
(Byte)flags,
ProtocolVersion,
bodyLength,
serial,
fields
);
return new Message(header, payload);
void AddField(FieldCode fieldCode, Object? value)
{
if (value is not null)
fields.Add(((Byte)fieldCode, value.Variant()));
}
}
internal static Message Read(DBusReader reader)
{
var header = ReadHeader(reader);
var (payload, error) = ReadPayload(reader, header);
return new Message(header, payload, error);
}
private static HeaderType ReadHeader(DBusReader reader)
{
return (HeaderType) HeaderSignature.Read(reader);
}
private static (Object? payload, Boolean parsingError) ReadPayload(DBusReader reader, HeaderType header)
{
var payloadLength = header.PayloadLength();
var signature = header.Signature();
if (payloadLength == 0 || signature is null)
return (null, false);
reader.AlignForComposite();
var raw = reader.ReadSegment((Int32)payloadLength);
var br = new DBusBufferReader(raw, reader.SwapEndian);
if (signature.IsEmpty)
return (raw.ToArray(), true); // no signature, but data available. should probably not happen.
// try
// {
return (signature.Read(br), false);
// }
// catch
// {
// Console.WriteLine("failed to parse payload");
// }
//return (raw.ToArray(), true); // parsing error
}
internal void Write(DBusWriter writer)
{
HeaderSignature.Write(Header, writer);
writer.AlignForComposite();
if (Payload is null) return;
Signature!.Write(Payload, writer);
// TODO: on debug reread written message with reader, Roundtrip
//
// var w = new DBusBufferWriter();
// HeaderSignature.Write(Header, w);
// writer.AlignForComposite();
//
// if (Payload is null) return;
// Signature!.Write(Payload, w);
//
// Console.WriteLine(w.Data.ToHexView());
//
// var r = new DBusBufferReader(w.Data, false);
// var m = Message.Read(r);
}
}