private void PortValueSingleEncoder_Decode <T>(string dataAsString, byte expectedPortId, PortModeInformationDataType expectedDataType, T[] expectedData) { var knowledge = new ProtocolKnowledge(); var serviceProvider = new ServiceCollection() .AddPoweredUp() .BuildServiceProvider(); KnowledgeManager.ApplyDynamicProtocolKnowledge(new HubAttachedIOForAttachedDeviceMessage(0x63, DeviceType.TechnicMediumHubTiltSensor, new Version("0.0.0.1"), new Version("0.0.0.1")) { HubId = 0 }, knowledge, serviceProvider.GetService <IDeviceFactory>()); // arrange var data = BytesStringUtil.StringToData(dataAsString); // act var message = MessageEncoder.Decode(data, knowledge) as PortValueSingleMessage; // assert Assert.Collection(message.Data, d => { Assert.Equal(d.PortId, expectedPortId); Assert.Equal(d.DataType, expectedDataType); if (d is PortValueData <T> actual) { Assert.Collection(actual.InputValues, expectedData.Cast <T>().Select <T, Action <T> >(expected => actual => Assert.Equal(expected, actual)).ToArray()); } } ); }
public void PortValueCombinedModeEncoder_Decode(string dataAsString, byte expectedPortId, byte[] expectedModes, PortModeInformationDataType[] expectedDataType, int[] expectedData) { var knowledge = new ProtocolKnowledge(); var serviceProvider = new ServiceCollection() .AddPoweredUp() .BuildServiceProvider(); KnowledgeManager.ApplyDynamicProtocolKnowledge(new HubAttachedIOForAttachedDeviceMessage(0x00, DeviceType.TechnicLargeLinearMotor, new Version("0.0.0.1"), new Version("0.0.0.1")) { HubId = 0 }, knowledge, serviceProvider.GetService <IDeviceFactory>()); KnowledgeManager.ApplyDynamicProtocolKnowledge(new PortInputFormatSetupCombinedModeForSetModeDataSetMessage( PortId: 0x00, CombinationIndex: 0, // should refer 0b0000_0000_0000_1110 => SPEED POS APOS ModeDataSets: new PortInputFormatSetupCombinedModeModeDataSet[] { new PortInputFormatSetupCombinedModeModeDataSet(0x01, 0), new PortInputFormatSetupCombinedModeModeDataSet(0x02, 0), new PortInputFormatSetupCombinedModeModeDataSet(0x03, 0), } ), knowledge, serviceProvider.GetService <IDeviceFactory>()); // arrange var data = BytesStringUtil.StringToData(dataAsString); // act var message = MessageEncoder.Decode(data, knowledge) as PortValueCombinedModeMessage; // assert Assert.Equal(expectedPortId, message.PortId); Assert.Collection(message.Data, Enumerable.Range(0, expectedModes.Length).Select <int, Action <PortValueData> >(pos => portValueData => { Assert.Equal(expectedPortId, portValueData.PortId); Assert.Equal(expectedDataType[pos], portValueData.DataType); Assert.Equal(expectedDataType[pos], portValueData.DataType); if (portValueData is PortValueData <sbyte> actual) { Assert.Equal(expectedData[pos], actual.InputValues[0]); } if (portValueData is PortValueData <short> actual2) { Assert.Equal(expectedData[pos], actual2.InputValues[0]); } if (portValueData is PortValueData <int> actual3) { Assert.Equal(expectedData[pos], actual3.InputValues[0]); } }).ToArray()); }
public static void PrettyPrintKnowledge(TextWriter writer, ProtocolKnowledge portKnowledge, bool showConfiguration = true) { string Intent(int depth) => " ".Substring(0, depth * 2); string MinMaxDescription(float rawMin, float rawMax, float min, float max) { bool isPassThrough = (rawMax == max) && (rawMin == min); bool isTranslation = (rawMax - rawMin) == (max - min); bool isScaling = !((rawMax == max) && (rawMin == min)); return((isPassThrough, isTranslation, isScaling) switch { (true, _, _) => " (pass-through)", (false, false, false) => string.Empty, (false, false, true) => " (scaling)", (false, true, false) => " (translation)", (false, true, true) => " (scaling, translation)", }); }
private static void ResetProtocolKnowledgeForPort(byte hubId, byte portId, ProtocolKnowledge knowledge) { var port = knowledge.Port(hubId, portId); port.IsDeviceConnected = false; port.IOTypeId = DeviceType.Unknown; port.HardwareRevision = new Version("0.0.0.0"); port.SoftwareRevision = new Version("0.0.0.0"); port.OutputCapability = false; port.InputCapability = false; port.LogicalCombinableCapability = false; port.LogicalSynchronizableCapability = false; port.Modes.Clear(); port.ModeCombinations = Array.Empty <ushort>(); port.UsedCombinationIndex = 0; port.MultiUpdateEnabled = false; port.ConfiguredModeDataSetIndex = Array.Empty <int>(); }
public static byte[] Encode(LegoWirelessMessage message, ProtocolKnowledge knowledge) { var messageType = message switch { HubPropertyMessage msg => MessageType.HubProperties, HubActionMessage msg => MessageType.HubActions, HubAlertMessage msg => MessageType.HubAlerts, HubAttachedIOMessage msg => MessageType.HubAttachedIO, GenericErrorMessage msg => MessageType.GenericErrorMessages, PortInformationRequestMessage msg => MessageType.PortInformationRequest, PortModeInformationRequestMessage msg => MessageType.PortModeInformationRequest, PortInputFormatSetupSingleMessage msg => MessageType.PortInputFormatSetupSingle, PortInputFormatSetupCombinedModeMessage msg => MessageType.PortInputFormatSetupCombinedMode, PortInformationMessage msg => MessageType.PortInformation, PortModeInformationMessage msg => MessageType.PortModeInformation, PortValueSingleMessage msg => MessageType.PortValueSingle, PortValueCombinedModeMessage msg => MessageType.PortValueCombinedMode, PortInputFormatSingleMessage msg => MessageType.PortInputFormatSingle, PortInputFormatCombinedModeMessage msg => MessageType.PortInputFormatCombinedMode, VirtualPortSetupMessage msg => MessageType.VirtualPortSetup, PortOutputCommandMessage msg => MessageType.PortOutputCommand, PortOutputCommandFeedbackMessage msg => MessageType.PortOutputCommandFeedback, _ => throw new NotImplementedException(), }; var encoder = CreateEncoder(messageType, knowledge); var contentLength = encoder.CalculateContentLength(message); var commonHeaderLength = (contentLength + 3 > 127) ? 4 : 3; byte[] data = new byte[commonHeaderLength + contentLength]; CommonMessageHeaderEncoder.Encode(contentLength, message.HubId, messageType, data.AsSpan().Slice(0, commonHeaderLength)); encoder.Encode(message, data.AsSpan().Slice(commonHeaderLength)); return(data); }
public PortValueSingleEncoder(ProtocolKnowledge knowledge) { _knowledge = knowledge; }
private static IMessageContentEncoder CreateEncoder(MessageType messageType, ProtocolKnowledge knowledge) => messageType switch {
public PortValueCombinedModeEncoder(ProtocolKnowledge knowledge) { _knowledge = knowledge; }
private static void AddCachePortAndPortModeInformation(DeviceType type, Version hardwareRevision, Version softwareRevision, HubInfo hub, PortInfo port, ProtocolKnowledge knowledge, IDeviceFactory deviceFactory) { var device = deviceFactory.Create(type); if (device is not null) { foreach (var message in device.GetStaticPortInfoMessages(hardwareRevision, softwareRevision, hub.SystemType).Select(b => MessageEncoder.Decode(b, null))) { var messageToProcess = message; switch (messageToProcess) { case PortModeInformationMessage pmim: messageToProcess = pmim with { HubId = port.HubId, PortId = port.PortId, }; break; case PortInformationMessage pim: messageToProcess = pim with { HubId = port.HubId, PortId = port.PortId, }; break; } ApplyStaticProtocolKnowledge(messageToProcess, knowledge); if (messageToProcess is PortModeInformationMessage pmim2 && pmim2.InformationType == PortModeInformationType.Name) { device.ExtendPortMode(knowledge.PortMode(pmim2.HubId, pmim2.PortId, pmim2.Mode)); } } } }
public static Task ApplyDynamicProtocolKnowledge(LegoWirelessMessage message, ProtocolKnowledge knowledge, IDeviceFactory deviceFactory) { HubInfo hub; PortInfo port; PortModeInfo mode; switch (message) { case HubPropertyMessage <SystemType> msg when msg.Property == HubProperty.SystemTypeId: hub = knowledge.Hub(msg.HubId); hub.SystemType = msg.Payload; break; case HubAttachedIOForAttachedDeviceMessage msg: hub = knowledge.Hub(msg.HubId); port = knowledge.Port(msg.HubId, msg.PortId); ResetProtocolKnowledgeForPort(msg.HubId, port.PortId, knowledge); port.IsDeviceConnected = true; port.IOTypeId = msg.IOTypeId; port.HardwareRevision = msg.HardwareRevision; port.SoftwareRevision = msg.SoftwareRevision; AddCachePortAndPortModeInformation(msg.IOTypeId, msg.HardwareRevision, msg.SoftwareRevision, hub, port, knowledge, deviceFactory); break; case HubAttachedIOForDetachedDeviceMessage msg: port = knowledge.Port(msg.HubId, msg.PortId); ResetProtocolKnowledgeForPort(msg.HubId, port.PortId, knowledge); port.IsDeviceConnected = false; break; case HubAttachedIOForAttachedVirtualDeviceMessage msg: hub = knowledge.Hub(msg.HubId); port = knowledge.Port(msg.HubId, msg.PortId); var partOfVirtual = knowledge.Port(msg.HubId, msg.PortAId); ResetProtocolKnowledgeForPort(msg.HubId, port.PortId, knowledge); port.IsDeviceConnected = true; port.IOTypeId = msg.IOTypeId; port.HardwareRevision = partOfVirtual.HardwareRevision; port.SoftwareRevision = partOfVirtual.SoftwareRevision; AddCachePortAndPortModeInformation(msg.IOTypeId, partOfVirtual.HardwareRevision, partOfVirtual.SoftwareRevision, hub, port, knowledge, deviceFactory); port.IsVirtual = true; port.PortAId = msg.PortAId; port.PortBId = msg.PortBId; break; case PortInputFormatSingleMessage msg: port = knowledge.Port(msg.HubId, msg.PortId); mode = knowledge.PortMode(msg.HubId, msg.PortId, msg.ModeIndex); port.LastFormattedPortMode = msg.ModeIndex; mode.DeltaInterval = msg.DeltaInterval; mode.NotificationEnabled = msg.NotificationEnabled; break; case PortInputFormatSetupCombinedModeForSetModeDataSetMessage msg: port = knowledge.Port(msg.HubId, msg.PortId); port.RequestedCombinedModeDataSets = msg.ModeDataSets; break; case PortInputFormatCombinedModeMessage msg: port = knowledge.Port(msg.HubId, msg.PortId); port.UsedCombinationIndex = msg.UsedCombinationIndex; port.MultiUpdateEnabled = msg.MultiUpdateEnabled; port.ConfiguredModeDataSetIndex = msg.ConfiguredModeDataSetIndex; break; } return(Task.CompletedTask); }
public static bool ApplyStaticProtocolKnowledge(LegoWirelessMessage message, ProtocolKnowledge knowledge) { var applicableMessage = true; PortInfo port; PortModeInfo mode; switch (message) { case PortInformationForModeInfoMessage msg: port = knowledge.Port(msg.HubId, msg.PortId); port.OutputCapability = msg.OutputCapability; port.InputCapability = msg.InputCapability; port.LogicalCombinableCapability = msg.LogicalCombinableCapability; port.LogicalSynchronizableCapability = msg.LogicalSynchronizableCapability; foreach (var modeInfo in Enumerable.Range(0, msg.TotalModeCount).Select(modeIndex => new PortModeInfo() { HubId = port.HubId, PortId = msg.PortId, ModeIndex = (byte)modeIndex, IsInput = ((1 << modeIndex) & msg.InputModes) > 0, IsOutput = ((1 << modeIndex) & msg.OutputModes) > 0 })) { port.Modes.TryAdd(modeInfo.ModeIndex, modeInfo); } break; case PortInformationForPossibleModeCombinationsMessage msg: port = knowledge.Port(msg.HubId, msg.PortId); port.ModeCombinations = msg.ModeCombinations; break; case PortModeInformationForNameMessage msg: mode = knowledge.PortMode(msg.HubId, msg.PortId, msg.Mode); mode.Name = msg.Name; break; case PortModeInformationForRawMessage msg: mode = knowledge.PortMode(msg.HubId, msg.PortId, msg.Mode); mode.RawMin = msg.RawMin; mode.RawMax = msg.RawMax; break; case PortModeInformationForPctMessage msg: mode = knowledge.PortMode(msg.HubId, msg.PortId, msg.Mode); mode.PctMin = msg.PctMin; mode.PctMax = msg.PctMax; break; case PortModeInformationForSIMessage msg: mode = knowledge.PortMode(msg.HubId, msg.PortId, msg.Mode); mode.SIMin = msg.SIMin; mode.SIMax = msg.SIMax; break; case PortModeInformationForSymbolMessage msg: mode = knowledge.PortMode(msg.HubId, msg.PortId, msg.Mode); mode.Symbol = msg.Symbol; break; case PortModeInformationForMappingMessage msg: mode = knowledge.PortMode(msg.HubId, msg.PortId, msg.Mode); mode.InputSupportsNull = msg.InputSupportsNull; mode.InputSupportFunctionalMapping20 = msg.InputSupportFunctionalMapping20; mode.InputAbsolute = msg.InputAbsolute; mode.InputRelative = msg.InputRelative; mode.InputDiscrete = msg.InputDiscrete; mode.OutputSupportsNull = msg.OutputSupportsNull; mode.OutputSupportFunctionalMapping20 = msg.OutputSupportFunctionalMapping20; mode.OutputAbsolute = msg.OutputAbsolute; mode.OutputRelative = msg.OutputRelative; mode.OutputDiscrete = msg.OutputDiscrete; break; case PortModeInformationForValueFormatMessage msg: mode = knowledge.PortMode(msg.HubId, msg.PortId, msg.Mode); mode.NumberOfDatasets = msg.NumberOfDatasets; mode.DatasetType = msg.DatasetType; mode.TotalFigures = msg.TotalFigures; mode.Decimals = msg.Decimals; break; default: applicableMessage = false; break; } return(applicableMessage); }
private static void PrettyPrintKnowledge(TextWriter writer, ProtocolKnowledge portKnowledge) { string Intent(int depth) => " ".Substring(0, depth * 2); foreach (var hub in portKnowledge.Hubs.OrderBy(h => h.HubId)) { Console.WriteLine($"{Intent(0)}Hub: {hub.HubId}"); foreach (var port in hub.Ports.Values.OrderBy(p => p.PortId)) { writer.WriteLine($"{Intent(1)}Port: {port.PortId}"); writer.WriteLine($"{Intent(2)}IOTypeId: {port.IOTypeId}"); writer.WriteLine($"{Intent(2)}HardwareRevision: {port.HardwareRevision}"); writer.WriteLine($"{Intent(2)}SoftwareRevision: {port.SoftwareRevision}"); writer.WriteLine($"{Intent(2)}OutputCapability: {port.OutputCapability}"); writer.WriteLine($"{Intent(2)}InputCapability: {port.InputCapability}"); writer.WriteLine($"{Intent(2)}LogicalCombinableCapability: {port.LogicalCombinableCapability}"); writer.WriteLine($"{Intent(2)}LogicalSynchronizableCapability: {port.LogicalSynchronizableCapability}"); // PortInformationForPossibleModeCombinationsMessage writer.WriteLine($"{Intent(2)}ModeCombinations: [{string.Join(", ", port.ModeCombinations.Select(x => BytesStringUtil.ToBitString(x)))}]"); writer.WriteLine($"{Intent(2)}UsedCombinationIndex: {port.UsedCombinationIndex}"); writer.WriteLine($"{Intent(2)}MultiUpdateEnabled: {port.MultiUpdateEnabled}"); writer.WriteLine($"{Intent(2)}ConfiguredModeDataSetIndex: [{string.Join(",", port.ConfiguredModeDataSetIndex)}]"); foreach (var mode in port.Modes.Values.OrderBy(m => m.ModeIndex)) { writer.WriteLine($"{Intent(2)}Mode: {mode.ModeIndex}"); writer.WriteLine($"{Intent(3)}Name: {mode.Name}"); writer.WriteLine($"{Intent(3)}IsInput: {mode.IsInput}"); writer.WriteLine($"{Intent(3)}IsOutput: {mode.IsOutput}"); writer.WriteLine($"{Intent(3)}RawMin: {mode.RawMin}"); writer.WriteLine($"{Intent(3)}RawMax: {mode.RawMax}"); // PortModeInformationForPctMessage writer.WriteLine($"{Intent(3)}PctMin: {mode.PctMin}"); writer.WriteLine($"{Intent(3)}PctMax: {mode.PctMax}"); // PortModeInformationForSIMessage writer.WriteLine($"{Intent(3)}SIMin: {mode.SIMin}"); writer.WriteLine($"{Intent(3)}SIMax: {mode.SIMax}"); // PortModeInformationForSymbolMessage writer.WriteLine($"{Intent(3)}Symbol: {mode.Symbol}"); // PortModeInformationForMappingMessage writer.WriteLine($"{Intent(3)}InputSupportsNull: {mode.InputSupportsNull}"); writer.WriteLine($"{Intent(3)}InputSupportFunctionalMapping20: {mode.InputSupportFunctionalMapping20}"); writer.WriteLine($"{Intent(3)}InputAbsolute: {mode.InputAbsolute}"); writer.WriteLine($"{Intent(3)}InputRelative: {mode.InputRelative}"); writer.WriteLine($"{Intent(3)}InputDiscrete: {mode.InputDiscrete}"); writer.WriteLine($"{Intent(3)}OutputSupportsNull: {mode.OutputSupportsNull}"); writer.WriteLine($"{Intent(3)}OutputSupportFunctionalMapping20: {mode.OutputSupportFunctionalMapping20}"); writer.WriteLine($"{Intent(3)}OutputAbsolute: {mode.OutputAbsolute}"); writer.WriteLine($"{Intent(3)}OutputRelative: {mode.OutputRelative}"); writer.WriteLine($"{Intent(3)}OutputDiscrete: {mode.OutputDiscrete}"); // PortModeInformationForValueFormatMessage writer.WriteLine($"{Intent(3)}NumberOfDatasets: {mode.NumberOfDatasets}"); writer.WriteLine($"{Intent(3)}DatasetType: {mode.DatasetType}"); writer.WriteLine($"{Intent(3)}TotalFigures: {mode.TotalFigures}"); writer.WriteLine($"{Intent(3)}Decimals: {mode.Decimals}"); // PortInputFormatSingleMessage writer.WriteLine($"{Intent(3)}DeltaInterval: {mode.DeltaInterval}"); writer.WriteLine($"{Intent(3)}NotificationEnabled: {mode.NotificationEnabled}"); } } } }