Created
February 21, 2023 15:31
-
-
Save bwedding/09ff9d825057f98dd6c5d4d9395bcc89 to your computer and use it in GitHub Desktop.
CanHeader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace SRHStressTester | |
{ | |
public enum HeartCommand | |
{ | |
StartHoming = 0, | |
StartPumping = 1, | |
GoToAuto = 2, | |
GoToManual = 3, | |
Stop = 4, | |
SendAllValues = 100 | |
} | |
public enum StrokeForm | |
{ | |
Linear, | |
Sine, | |
FastSine | |
} | |
public enum MessagePriority { Default = 5 }; | |
public enum MessageType { Undefined = 0, ByteValue = 1, BooleanValue = 2, TextMessage = 3, PhasePortions = 4, StrokeTiming = 5, Int32Value = 6 }; | |
public enum DataSource { Right = 0, Left = 1, ControlUnit = 2, PCApplication = 3 } | |
public enum StatusMessageLevel { None = 0, Error = 64, Warning = 65, Info = 66, InvalidCommand = 67 } | |
public enum OperationState | |
{ | |
Undefined = 0, | |
Init = 1, | |
Startup = 3, | |
Homing = 4, | |
HomingDone = 5, | |
Manual = 7, | |
Auto = 8, | |
HomingFailed = 9, | |
InitFailed = 10, | |
Stopped = 11 | |
} | |
public enum AlivenessState | |
{ | |
Dead = 0, | |
Alive = 1 | |
} | |
public enum Value | |
{ | |
// Common values | |
NoMessage = 0, | |
Command = 1, // ByteValue | |
PhasePortions = 4, // CanMessagePhasePortions | |
OperationMode = 6, // ByteValue | |
LoggingMode = 7, // BooleanValue | |
StrokeCount = 8, // Int32Value | |
CpuLoad = 9, // ByteValue | |
StatusCode = 10, // ByteValue StatusCodeValue | |
HeartRunning = 11, // ByteValue | |
LeftMCUStatus = 12, // ByteValue | |
RightMCUStatus = 13, // ByteValue | |
UseMedicalSensor = 14, // BooleanValue | |
StopI2CCommunication = 15, // BooleanValue | |
ExternalRelease = 16, // Int32 Value | |
HCBRelease = 17, // Int32 Value | |
PumpID = 18, // Int32 Value | |
BeatsPerformed = 19, // Int32Value | |
StrokeTiming = 20, // CanMessageStrokeTiming | |
StrokeForm = 21, // ByteValue (Linear, Sine) enum StrokeForm | |
ActualMechanicalVersion = 22, // ByteValue (V11B or V11C) | |
CurrentConsumption = 23, // Int32Value ?? | |
StrokeLengthActual = 24, // Int32Value | |
HeartRateActual = 25, // ByteValue | |
PowerConsumption = 26, // Int32Value | |
BatteryPower = 28, // Int32Value | |
InstantaneousPower = 29, // Int32Value | |
AVPlanePosition = 30, // Int32Value | |
DesiredCurrent = 31, // Int32Value | |
Time = 32, | |
GraphOutputMode = 33, // BooleanValue | |
MaxPowerConsumption = 43, // Int32Value | |
AverageDesiredCurrent = 44, // Int32Value | |
MaxDesiredCurrent = 45, // Int32Value | |
InstantaneousCurrent = 46, // Int32Value | |
DesiredPosition = 50, // Int32Value | |
InstantaneousVoltage = 51, // Int32Value | |
Vbus = 52, | |
NewInstantaneousPower = 53, // Int32Value | |
NewMaxPower = 54, // Int32Value | |
NewAveragePower = 55, // Int32Value | |
QCurrent = 56, // Int32Value | |
QCurrentSystolicAvg = 57, // Int32Value | |
QCurrentMax = 58, // Int32Value | |
EstimatedPressure = 59, // Int32Value | |
PowerLosses = 60, // Int32Value | |
InvalidCommand = 67, // Received when the heart has received an invalid command | |
FlowLimit = 68, // Int32Value | |
FlowLimitState = 69, // ByteValue "Decrease", 1 "Allow increasing", 2 "Keep constant", 3 | |
StartCalibration = 72, // BooleanValue | |
Check_Serializer_Errors = 73, // BooleanValue | |
PhaseCurrentMax = 74, // Int32Value | |
PhaseCurrentAvg = 75, // Int32Value | |
TriggerResetLeft = 76, // BooleanValue | |
TriggerResetRight = 77, // BooleanValue | |
UseManualValue = 78, // BooleanValue | |
AtmosphericPressure = 79, // Int32Value | |
// Left side values | |
LeftStrokeLengthTarget = 100, // Int32Value | |
LeftStrokeVolumeTarget = 103, // Int32Value | |
LeftDesiredAtriumPressure = 104, // Int32Value | |
LeftOutput = 105, // Int32Value | |
LeftAtriumPressure = 106, // Int32Value | |
LeftTemperature = 107, // Int32Value | |
LeftCalPressure = 108, // Int32Value | |
LeftMedicalPressure = 109, // Int32Value | |
LeftIntPressure = 110, // Int32Value | |
LeftOffset = 111, // Int32Value | |
LeftSensorTemperature = 112, // Int32Value | |
LeftGain = 113, // Int32Value | |
// Right side values | |
RightStrokeLengthTarget = 200, // Int32Value | |
RightStrokeVolumeTarget = 203, // Int32Value | |
RightDesiredAtriumPressure = 204, // Int32Value | |
RightOutput = 205, // Int32Value | |
RightAtriumPressure = 206, // Int32Value | |
RightTemperature = 207, // Int32Value | |
RightCalPressure = 208, // Int32Value | |
RightMedicalPressure = 209, // Int32Value | |
RightIntPressure = 210, // Int32Value | |
RightOffset = 211, // Int32Value | |
RightSensorTemperature = 212, // Int32Value | |
RightGain = 213 // Int32Value | |
} | |
public enum StatusCodeValue | |
{ | |
Unknown = 0, | |
SystemStarted = 1, | |
RecoveryAfterReset = 2, | |
WatchdogReset = 3, | |
HallSensorFault = 4, | |
RuntimeException = 5, | |
DriverCircuitFault = 6, | |
TopEndpointNotFound = 7, | |
BottomEndpointNotFound = 8, | |
SoftwareReset = 9, | |
HeartSyncFault = 10, | |
DriverCircuitWarning = 11, | |
InsufficientVoltage = 12, | |
TruncatedPIDOutput = 13 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Runtime.Remoting.Messaging; | |
namespace SRHStressTester | |
{ | |
public class CanHeader | |
{ | |
private const int PRIO_BITS = 0x700; | |
private const int PRIO_OFFSET = 8; | |
private const int MESSAGE_TYPE_BITS = 0x38; | |
private const int MESSAGE_TYPE_OFFSET = 3; | |
private const int DATA_SOURCE_BITS = 0x3; | |
private const int DATA_SOURCE_OFFSET = 0; | |
public CanHeader(int id) | |
{ | |
ParsePrio(id); | |
ParseMessageType(id); | |
ParseSource(id); | |
} | |
private void ParsePrio(int id) | |
{ | |
int tmp = (id & PRIO_BITS) >> PRIO_OFFSET; | |
if (Enum.IsDefined(typeof(MessagePriority), tmp)) | |
{ | |
Prio = (MessagePriority)tmp; | |
} | |
else | |
{ | |
Prio = MessagePriority.Default; | |
} | |
} | |
private void ParseMessageType(int id) | |
{ | |
int tmp = (id & MESSAGE_TYPE_BITS) >> MESSAGE_TYPE_OFFSET; | |
if (Enum.IsDefined(typeof(MessageType), tmp)) | |
{ | |
MessageType = (MessageType)tmp; | |
} | |
else | |
{ | |
MessageType = MessageType.Undefined; | |
IsInvalid = true; | |
//ErrorHandler.Log(ErrorType.Error, $"Unkown message type {tmp} received!"); | |
} | |
} | |
private void ParseSource(int id) | |
{ | |
int tmp = (id & DATA_SOURCE_BITS) >> DATA_SOURCE_OFFSET; | |
if (Enum.IsDefined(typeof(DataSource), tmp)) | |
{ | |
Source = (DataSource)tmp; | |
} | |
else | |
{ | |
Source = DataSource.Left; | |
IsInvalid = true; | |
//ErrorHandler.Log(ErrorType.Error, $"Unkown data source {tmp} received!"); | |
} | |
} | |
public CanHeader(MessagePriority prio, MessageType messageType, DataSource dataSource) | |
{ | |
Prio = prio; | |
MessageType = messageType; | |
Source = dataSource; | |
} | |
public int RawHeader | |
{ | |
get | |
{ | |
int result = 0; | |
result = result | ((int)Prio << PRIO_OFFSET); | |
result = result | ((int)Source << DATA_SOURCE_OFFSET); | |
result = result | ((int)MessageType << MESSAGE_TYPE_OFFSET); | |
return result; | |
} | |
} | |
public MessagePriority Prio { get; private set; } | |
public DataSource Source { get; private set; } | |
public MessageType MessageType { get; private set; } | |
public bool IsInvalid { get; private set; } | |
} | |
public abstract class CanMessage : IMessage | |
{ | |
protected CanMessage(CanHeader header, byte[] data) | |
{ | |
Header = header; | |
RawData = data; | |
} | |
public CanHeader Header { get; private set; } | |
public byte[] RawData { get; protected set; } | |
public abstract byte ValueId { get; } | |
public MessagePriority Prio => Header.Prio; | |
public DataSource Source => Header.Source; | |
public MessageType MessageType => Header.MessageType; | |
public bool IsInvalid => Header.IsInvalid; | |
} | |
public class CanMessageBool : CanMessage, IBoolMessage | |
{ | |
private readonly byte valueId; | |
public CanMessageBool(CanHeader header, byte[] data) : base(header, data) | |
{ | |
if (RawData.Length >= 2) | |
{ | |
valueId = RawData[0]; | |
Value = RawData[1] > 0; | |
} | |
} | |
public override byte ValueId => valueId; | |
public bool Value { get; private set; } | |
} | |
public class CanMessageByte : CanMessage, IByteMessage | |
{ | |
private byte valueId; | |
public CanMessageByte(CanHeader header, byte[] data) : base(header, data) | |
{ | |
if (RawData.Length >= 2) | |
{ | |
valueId = RawData[0]; | |
Value = RawData[1]; | |
} | |
else | |
{ | |
valueId = 0; | |
Value = 0; | |
} | |
} | |
public override byte ValueId => valueId; | |
public byte Value { get; private set; } | |
} | |
public class CanMessageInt32 : CanMessage, IInt32Message | |
{ | |
private readonly byte _valueId; | |
public CanMessageInt32(CanHeader header, byte[] data) : base(header, data) | |
{ | |
if (RawData.Length >= 8) | |
{ | |
_valueId = RawData[0]; | |
Value = BitConverter.ToInt32(RawData, 1); | |
} | |
else | |
{ | |
_valueId = 0; | |
Value = 0; | |
} | |
} | |
public CanMessageInt32(CanHeader header, byte valueId, int value) : base(header, new byte[8]) | |
{ | |
List<byte> rawData = new List<byte>(); | |
rawData.Add(valueId); | |
rawData.AddRange(BitConverter.GetBytes(value)); | |
RawData = rawData.ToArray(); | |
_valueId = RawData[0]; | |
Value = BitConverter.ToInt32(RawData, 1); | |
} | |
public override byte ValueId => _valueId; | |
public int Value { get; private set; } | |
} | |
public class CanMessageString : CanMessage, IStringMessage | |
{ | |
public CanMessageString(CanHeader header, byte[] data) : base(header, data) | |
{ | |
if (Enum.IsDefined(typeof(StatusMessageLevel), (int)RawData[0])) | |
{ | |
Type = (StatusMessageLevel)RawData[0]; | |
} | |
else | |
{ | |
Type = StatusMessageLevel.InvalidCommand; | |
} | |
EndOfMessage = false; | |
int endOfStringIndex = RawData.Length; | |
for (int i = 1; i < RawData.Length; i++) | |
{ | |
if (RawData[i] == 0) | |
{ | |
EndOfMessage = true; | |
endOfStringIndex = i; | |
break; | |
} | |
} | |
Value = System.Text.Encoding.Default.GetString(RawData, 1, endOfStringIndex - 1); | |
} | |
public override byte ValueId => 0; | |
public StatusMessageLevel Type { get; private set; } | |
public string Value { get; private set; } | |
public bool EndOfMessage { get; private set; } | |
} | |
public class CanMessageStrokeTiming : CanMessage, IStrokeTimingMessage | |
{ | |
private readonly byte valueId; | |
public CanMessageStrokeTiming(CanHeader header, byte[] data) : base(header, data) | |
{ | |
valueId = RawData[0]; | |
Systole = BitConverter.ToUInt16(RawData, 1); | |
SystolePause = RawData[3]; | |
Diastole = BitConverter.ToUInt16(RawData, 4); | |
DiastolePause = RawData[6]; | |
} | |
public CanMessageStrokeTiming(CanHeader header, byte valueId, ushort systole, byte systolePause, | |
ushort diastole, byte diastolePause) : base(header, new byte[8]) | |
{ | |
this.valueId = RawData[0] = valueId; | |
Array.Copy(BitConverter.GetBytes(systole), 0, RawData, 1, 2); | |
SystolePause = RawData[3] = systolePause; | |
Array.Copy(BitConverter.GetBytes(diastole), 0, RawData, 4, 2); | |
DiastolePause = RawData[6] = diastolePause; | |
Systole = BitConverter.ToUInt16(RawData, 1); | |
Diastole = BitConverter.ToUInt16(RawData, 4); | |
} | |
public ushort Systole { get; protected set; } | |
public byte SystolePause { get; protected set; } | |
public ushort Diastole { get; protected set; } | |
public byte DiastolePause { get; protected set; } | |
public override byte ValueId => valueId; | |
} | |
public class TextMessage : ITextMessage | |
{ | |
public TextMessage(StatusMessageLevel type, string text, DataSource source) | |
{ | |
TimeStamp = DateTime.Now; | |
MessageType = type; | |
Message = text; | |
Source = source; | |
} | |
public DataSource Source | |
{ | |
get; | |
private set; | |
} | |
public DateTime TimeStamp | |
{ | |
get; | |
private set; | |
} | |
public string Message | |
{ | |
get; | |
private set; | |
} | |
public StatusMessageLevel MessageType | |
{ | |
get; | |
private set; | |
} | |
} | |
public class MessageReceivedEventArgs<T> : EventArgs | |
{ | |
public MessageReceivedEventArgs(T msg) | |
{ | |
Message = msg; | |
} | |
public T Message { get; private set; } | |
} | |
public class CanTextMessageBuilder | |
{ | |
private string currentString = String.Empty; | |
public CanTextMessageBuilder(DataSource dataSource) | |
{ | |
Source = dataSource; | |
} | |
public DataSource Source { get; private set; } | |
public event EventHandler<MessageReceivedEventArgs<ITextMessage>> MessageReceived = delegate { }; | |
} | |
public class CanMessagePhasePortions : CanMessage, IPhasePortionsMessage | |
{ | |
private readonly byte valueId; | |
public CanMessagePhasePortions(CanHeader header, byte[] data) : base(header, data) | |
{ | |
if (RawData.Length >= 3) | |
{ | |
valueId = RawData[0]; | |
Systole = RawData[1]; | |
Diastole = RawData[2]; | |
} | |
} | |
public override byte ValueId => valueId; | |
public byte Diastole { get; private set; } | |
public byte Systole { get; private set; } | |
} | |
public class CanMessageParser | |
{ | |
public CanHeader ReadHeader(int header) | |
{ | |
return new CanHeader(header); | |
} | |
public CanMessageByte ParseByteMessage(CanHeader header, byte[] data) | |
{ | |
var value = new CanMessageByte(header, data); | |
return value; | |
} | |
public CanMessageInt32 ParseInt32Message(CanHeader header, byte[] data) | |
{ | |
var value = new CanMessageInt32(header, data); | |
return value; | |
} | |
public CanMessageBool ParseBoolMessage(CanHeader header, byte[] data) | |
{ | |
var value = new CanMessageBool(header, data); | |
return value; | |
} | |
public CanMessageString ParseTextMessage(CanHeader header, byte[] data) | |
{ | |
var value = new CanMessageString(header, data); | |
return value; | |
} | |
public CanMessagePhasePortions ParsePhasePortionsMessage(CanHeader header, byte[] data) | |
{ | |
var value = new CanMessagePhasePortions(header, data); | |
return value; | |
} | |
public CanMessageStrokeTiming ParseStrokeTimingMessage(CanHeader header, byte[] data) | |
{ | |
var value = new CanMessageStrokeTiming(header, data); | |
return value; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace SRHStressTester | |
{ | |
public interface IAlivenessHandler | |
{ | |
AlivenessState CurrentAlivenessState(DataSource source); | |
event EventHandler<AlivenessState> AlivenessChangedEvent; | |
event EventHandler<ITextMessage> MessageReceived; | |
} | |
public interface IMessage | |
{ | |
MessagePriority Prio { get; } | |
DataSource Source { get; } | |
MessageType MessageType { get; } | |
bool IsInvalid { get; } | |
} | |
public interface IValueMessage : IMessage | |
{ | |
byte ValueId { get; } | |
} | |
public interface IBoolMessage : IValueMessage | |
{ | |
bool Value { get; } | |
} | |
public interface IByteMessage : IValueMessage | |
{ | |
byte Value { get; } | |
} | |
public interface IInt32Message : IValueMessage | |
{ | |
int Value { get; } | |
} | |
public interface IStringMessage : IMessage | |
{ | |
StatusMessageLevel Type { get; } | |
string Value { get; } | |
bool EndOfMessage { get; } | |
} | |
public interface IStrokeTiming | |
{ | |
ushort Systole { get; } | |
byte SystolePause { get; } | |
ushort Diastole { get; } | |
byte DiastolePause { get; } | |
} | |
public interface IStrokeTimingMessage : IValueMessage, IStrokeTiming | |
{ } | |
public interface ITextMessage | |
{ | |
DataSource Source { get; } | |
DateTime TimeStamp { get; } | |
string Message { get; } | |
StatusMessageLevel MessageType { get; } | |
} | |
public interface IPhasePortionsMessage : IValueMessage | |
{ | |
byte Diastole { get; } | |
byte Systole { get; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment