Innovenergy_trunk/csharp/Lib/Protocols/Modbus/Channels/RemoteSerialChannel.cs

300 lines
9.2 KiB
C#

using System.Diagnostics.CodeAnalysis;
using System.IO.Ports;
using System.Reactive.Linq;
using CliWrap;
using InnovEnergy.Lib.Utils;
/*namespace InnovEnergy.Lib.Protocols.Modbus.Channels;
public record RemoteSerialConnection
(
Func<Int32, IReadOnlyList<Byte>> Read,
Action<IReadOnlyList<Byte>> Write,
Action Close
);
// public class RemoteSerialChannel : ConnectionChannel<RemoteSerialConnection>
// {
// private readonly Command _Command;
//
// public RemoteSerialChannel(SshHost host,
// String tty,
// Int32 baudRate,
// Parity parity,
// Int32 stopBits,
// Int32 dataBits)
// {
// tty = tty.EnsureStartsWith("/dev/");
//
// var configureTty = ConfigureTty(tty, baudRate, parity, stopBits, dataBits);
// var redirectStreams = RedirectStreams(tty);
//
// var call = $"{configureTty}; {redirectStreams}";
//
// _Command = host
// .Command
// .AppendArgument(call);
//
// _Command.WriteLine();
// }
//
//
// protected override RemoteSerialConnection Open()
// {
// var observableProcess = new ObservableProcess(_Command);
//
// observableProcess.Start();
//
//
// IReadOnlyList<Byte> Read(Int32 i)
// {
// return observableProcess.Read(i).Result;
// }
//
// void Write(IReadOnlyList<Byte> data) => observableProcess.StdIn.OnNext(data.ToArray());
//
// return new RemoteSerialConnection(Read, Write, observableProcess.Interrupt);
// }
//
// protected override void Close(RemoteSerialConnection connection)
// {
// connection.Close();
// }
//
// protected override IReadOnlyList<Byte> Read(RemoteSerialConnection connection, Int32 nBytes)
// {
// return connection.Read(nBytes);
// }
//
// protected override void Write(RemoteSerialConnection connection, IReadOnlyList<Byte> data)
// {
// connection.Write(data);
// }
//
//
// private static String RedirectStreams(String tty)
// {
// // https://unix.stackexchange.com/questions/19604/all-about-ssh-proxycommand
//
// return $"exec 3<>{tty}; " +
// $"cat <&3 & cat >&3; " +
// // $"(cat <&3 | tee -a ~/read) & cat | tee -a ~/write >&3; " +
// $"kill $!";
//
// // var call = $"trap 'kill -HUP $(jobs -lp) 2>/dev/null || true' EXIT; " +
// // $"{configure} ; "+
// // $"dd if={tty} of=/dev/stdout bs=1 & " +
// // $"dd if=/dev/stdin of={tty} bs=1 ;"
// }
//
// [SuppressMessage("ReSharper", "StringLiteralTypo")]
// private static String ConfigureTty(String tty, Int32 baudRate, Parity parity, Int32 stopBits, Int32 dataBits)
// {
// var oParity = parity switch
// {
// Parity.Even => "parenb -parodd",
// Parity.Odd => "parenb parodd",
// Parity.None => "-parenb",
// _ => throw new NotImplementedException()
// };
//
// var oStopBits = stopBits switch
// {
// 1 => "-cstopb",
// 2 => "cstopb",
// _ => throw new NotImplementedException()
// };
//
// var oDataBits = "cs" + dataBits;
//
// return $"stty -F {tty} {baudRate} {oDataBits} {oStopBits} {oParity}";
// }
//
// }
public class RemoteSerialChannel : ConnectionChannel<TcpChannel>
{
private readonly Command _Command;
private readonly TcpChannel _TcpChannel;
const String SsDir = "/opt/victronenergy/serial-starter";
const String KillTasks = "kill $!";
private CancellationTokenSource _CancellationTokenSource = new CancellationTokenSource();
private CommandTask<CommandResult>? _CommandTask;
public RemoteSerialChannel(SshHost host,
String tty,
Int32 baudRate,
Parity parity,
Int32 dataBits,
Int32 stopBits)
{
const Int32 port = 6855;
tty = tty.EnsureStartsWith("/dev/");
var configureTty = ConfigureTty(tty, baudRate, parity, stopBits, dataBits);
var stopTty = $"{SsDir}/stop-tty.sh {tty}";
var startTty = $"{SsDir}/start-tty.sh {tty}";
// ReSharper disable once StringLiteralTypo
var socat = $"socat TCP-LISTEN:{port},nodelay {tty},raw";
//var script = $"-n -o RemoteCommand='{stopTty}; {configureTty} && {socat} ; {startTty} ; {KillTasks}'";
//var script = $"{stopTty}; {configureTty} && {socat} ; {startTty} ; {KillTasks}";
//var script = $"{configureTty} && {socat} ; {KillTasks}";
var script = $"{configureTty} && {socat}";
_Command = host.Command.AppendArgument(script);
_Command.WriteLine();
_TcpChannel = new TcpChannel(host.HostName, port);
}
private static String ConfigureTty(String tty, Int32 baudRate, Parity parity, Int32 stopBits, Int32 dataBits)
{
var oParity = parity switch
{
Parity.Even => "parenb -parodd",
Parity.Odd => "parenb parodd",
Parity.None => "-parenb",
_ => throw new NotImplementedException()
};
var oStopBits = stopBits switch
{
1 => "-cstopb",
2 => "cstopb",
_ => throw new NotImplementedException()
};
var oDataBits = "cs" + dataBits;
return $"stty -F {tty} {baudRate} {oDataBits} {oStopBits} {oParity}";
}
protected override TcpChannel Open()
{
//_CommandTask ??= _Command.ExecuteAsync(_CancellationTokenSource.Token);
//Thread.Sleep(2000); // wait until socat is ready
return _TcpChannel;
}
protected override void Close(TcpChannel connection)
{
_CancellationTokenSource.Cancel();
connection.Dispose();
_CommandTask = null;
_CancellationTokenSource = new CancellationTokenSource();
}
protected override IReadOnlyList<Byte> Read(TcpChannel connection, Int32 nBytes)
{
return connection.Read(nBytes);
}
protected override void Write(TcpChannel connection, IReadOnlyList<Byte> data)
{
connection.Write(data);
}
}*/
using System;
using System.IO.Ports;
using CliWrap;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.Protocols.Modbus.Channels
{
public class RemoteSerialChannel : ConnectionChannel<TcpChannel>, IDisposable
{
private readonly Command _Command;
private readonly TcpChannel _TcpChannel;
const string SsDir = "/opt/victronenergy/serial-starter";
const string KillTasks = "kill $!";
private CancellationTokenSource _CancellationTokenSource = new CancellationTokenSource();
private CommandTask<CommandResult>? _CommandTask;
public RemoteSerialChannel(SshHost host, string tty, int baudRate, Parity parity, int dataBits, int stopBits)
{
const int port = 6855;
tty = tty.EnsureStartsWith("/dev/");
var configureTty = ConfigureTty(tty, baudRate, parity, stopBits, dataBits);
var stopTty = $"{SsDir}/stop-tty.sh {tty}";
var startTty = $"{SsDir}/start-tty.sh {tty}";
var socat = $"socat TCP-LISTEN:{port},nodelay {tty},raw";
var script = $"{configureTty} && {socat}";
_Command = host.Command.AppendArgument(script);
_TcpChannel = new TcpChannel(host.HostName, port);
}
private static string ConfigureTty(string tty, int baudRate, Parity parity, int stopBits, int dataBits)
{
var oParity = parity switch
{
Parity.Even => "parenb -parodd",
Parity.Odd => "parenb parodd",
Parity.None => "-parenb",
_ => throw new NotImplementedException()
};
var oStopBits = stopBits switch
{
1 => "-cstopb",
2 => "cstopb",
_ => throw new NotImplementedException()
};
var oDataBits = "cs" + dataBits;
return $"stty -F {tty} {baudRate} {oDataBits} {oStopBits} {oParity}";
}
protected override TcpChannel Open()
{
return _TcpChannel;
}
protected override void Close(TcpChannel connection)
{
_CancellationTokenSource.Cancel();
connection.Dispose();
_CancellationTokenSource = new CancellationTokenSource();
}
protected override IReadOnlyList<byte> Read(TcpChannel connection, int nBytes)
{
return connection.Read(nBytes);
}
protected override void Write(TcpChannel connection, IReadOnlyList<byte> data)
{
connection.Write(data);
}
public void Dispose()
{
Close(_TcpChannel);
}
}
}