221 lines
7.0 KiB
C#
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);
|
|
}
|
|
|
|
} |