From 8f3eb39aeb9f20264968f3a2b82122ed787c6e37 Mon Sep 17 00:00:00 2001 From: Atsushi Eno Date: Mon, 4 Mar 2024 05:31:58 +0900 Subject: [PATCH] Now we have PropertyClient and have way simpler ClientConnection class. I could name them as PropertyClientHostFacade and ProfileClientHostFacace, but I guess they are simpler. --- .../ktmidi/citool/ClientConnectionModel.kt | 20 +- .../ktmidi/citool/view/ViewModel.kt | 2 +- ktmidi-ci/api/android/ktmidi-ci.api | 60 +++-- ktmidi-ci/api/jvm/ktmidi-ci.api | 60 +++-- .../atsushieno/ktmidi/ci/ClientConnection.kt | 218 ++++++++++-------- .../dev/atsushieno/ktmidi/ci/Messenger.kt | 41 ++-- .../PropertyCommonRules.Client.kt | 2 +- .../ci/PropertyExchangeHostFacadeTest.kt | 39 ++-- 8 files changed, 220 insertions(+), 222 deletions(-) diff --git a/ktmidi-ci-tool/src/commonMain/kotlin/dev/atsushieno/ktmidi/citool/ClientConnectionModel.kt b/ktmidi-ci-tool/src/commonMain/kotlin/dev/atsushieno/ktmidi/citool/ClientConnectionModel.kt index cc3f4abad..eba59114c 100644 --- a/ktmidi-ci-tool/src/commonMain/kotlin/dev/atsushieno/ktmidi/citool/ClientConnectionModel.kt +++ b/ktmidi-ci-tool/src/commonMain/kotlin/dev/atsushieno/ktmidi/citool/ClientConnectionModel.kt @@ -23,21 +23,21 @@ class ClientConnectionModel(val parent: CIDeviceModel, val conn: ClientConnectio var deviceInfo = mutableStateOf(conn.deviceInfo) - val properties = mutableStateListOf().apply { addAll(conn.properties.values)} + val properties = mutableStateListOf().apply { addAll(conn.propertyClient.properties.values)} - fun getMetadataList() = conn.propertyClient.getMetadataList() + fun getMetadataList() = conn.propertyRules.getMetadataList() data class SubscriptionState(val propertyId: String, var state: MutableState) var subscriptions = mutableStateListOf() fun getPropertyData(resource: String, encoding: String?, paginateOffset: Int?, paginateLimit: Int?) = - conn.sendGetPropertyData(resource, encoding, paginateOffset, paginateLimit) + conn.propertyClient.sendGetPropertyData(resource, encoding, paginateOffset, paginateLimit) fun setPropertyData(resource: String, data: List, encoding: String?, isPartial: Boolean) = - conn.sendSetPropertyData(resource, data, encoding, isPartial) + conn.propertyClient.sendSetPropertyData(resource, data, encoding, isPartial) fun subscribeProperty(resource: String, mutualEncoding: String?) = - conn.sendSubscribeProperty(resource, mutualEncoding) + conn.propertyClient.sendSubscribeProperty(resource, mutualEncoding) fun unsubscribeProperty(resource: String) = - conn.sendUnsubscribeProperty(resource) + conn.propertyClient.sendUnsubscribeProperty(resource) fun requestMidiMessageReport(address: Byte, targetMUID: Int, messageDataControl: Byte = MidiMessageReportDataControl.Full, @@ -73,7 +73,7 @@ class ClientConnectionModel(val parent: CIDeviceModel, val conn: ClientConnectio } } - conn.properties.valueUpdated.add { entry -> + conn.propertyClient.properties.valueUpdated.add { entry -> val index = properties.indexOfFirst { it.id == entry.id } if (index < 0) properties.add(entry) @@ -85,12 +85,12 @@ class ClientConnectionModel(val parent: CIDeviceModel, val conn: ClientConnectio deviceInfo.value = conn.deviceInfo } - conn.properties.propertiesCatalogUpdated.add { + conn.propertyClient.properties.propertiesCatalogUpdated.add { properties.clear() - properties.addAll(conn.properties.values) + properties.addAll(conn.propertyClient.properties.values) } - conn.subscriptionUpdated.add { sub -> + conn.propertyClient.subscriptionUpdated.add { sub -> if (sub.state == SubscriptionActionState.Subscribing) subscriptions.add(SubscriptionState(sub.propertyId, mutableStateOf(sub.state))) else { diff --git a/ktmidi-ci-tool/src/commonMain/kotlin/dev/atsushieno/ktmidi/citool/view/ViewModel.kt b/ktmidi-ci-tool/src/commonMain/kotlin/dev/atsushieno/ktmidi/citool/view/ViewModel.kt index 7ac69e091..a0342f2e1 100644 --- a/ktmidi-ci-tool/src/commonMain/kotlin/dev/atsushieno/ktmidi/citool/view/ViewModel.kt +++ b/ktmidi-ci-tool/src/commonMain/kotlin/dev/atsushieno/ktmidi/citool/view/ViewModel.kt @@ -78,7 +78,7 @@ class ConnectionViewModel(val conn: ClientConnectionModel) { fun selectProperty(propertyId: String) { Snapshot.withMutableSnapshot { selectedProperty.value = propertyId } - val metadata = conn.conn.propertyClient.getMetadataList()?.firstOrNull { it.propertyId == propertyId } + val metadata = conn.conn.propertyRules.getMetadataList()?.firstOrNull { it.propertyId == propertyId } as CommonRulesPropertyMetadata conn.getPropertyData(propertyId, encoding = metadata.encodings.firstOrNull(), paginateOffset = 0, paginateLimit = 10) } diff --git a/ktmidi-ci/api/android/ktmidi-ci.api b/ktmidi-ci/api/android/ktmidi-ci.api index 0bbb46456..e53a4091f 100644 --- a/ktmidi-ci/api/android/ktmidi-ci.api +++ b/ktmidi-ci/api/android/ktmidi-ci.api @@ -117,44 +117,15 @@ public final class dev/atsushieno/ktmidi/ci/ClientConnection { public synthetic fun (Ldev/atsushieno/ktmidi/ci/MidiCIDevice;ILdev/atsushieno/ktmidi/ci/DeviceDetails;BLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getDeviceInfo ()Ldev/atsushieno/ktmidi/ci/MidiCIDeviceInfo; public final fun getMaxSimultaneousPropertyRequests ()B - public final fun getPendingChunkManager ()Ldev/atsushieno/ktmidi/ci/PropertyChunkManager; public final fun getProductInstanceId ()Ljava/lang/String; - public final fun getProfileClient ()Ldev/atsushieno/ktmidi/ci/ClientConnection$ProfileClient; - public final fun getProperties ()Ldev/atsushieno/ktmidi/ci/ClientObservablePropertyList; - public final fun getPropertyClient ()Ldev/atsushieno/ktmidi/ci/MidiCIClientPropertyRules; - public final fun getSubscriptionUpdated ()Ljava/util/List; - public final fun getSubscriptions ()Ljava/util/List; + public final fun getProfileClient ()Ldev/atsushieno/ktmidi/ci/ProfileClient; + public final fun getPropertyClient ()Ldev/atsushieno/ktmidi/ci/PropertyClient; + public final fun getPropertyRules ()Ldev/atsushieno/ktmidi/ci/MidiCIClientPropertyRules; public final fun getTargetMUID ()I - public final fun processGetDataReply (Ldev/atsushieno/ktmidi/ci/Message$GetPropertyDataReply;)V - public final fun processPropertyCapabilitiesReply (Ldev/atsushieno/ktmidi/ci/Message$PropertyGetCapabilitiesReply;)V - public final fun processPropertySubscriptionReply (Ldev/atsushieno/ktmidi/ci/Message$SubscribePropertyReply;)V - public final fun processSubscribeProperty (Ldev/atsushieno/ktmidi/ci/Message$SubscribeProperty;)V - public final fun sendGetPropertyData (Ldev/atsushieno/ktmidi/ci/Message$GetPropertyData;)V - public final fun sendGetPropertyData (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;)V - public static synthetic fun sendGetPropertyData$default (Ldev/atsushieno/ktmidi/ci/ClientConnection;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)V - public final fun sendSetPropertyData (Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Z)V - public final fun sendSetPropertyData (Ljava/util/List;Ljava/util/List;)V - public static synthetic fun sendSetPropertyData$default (Ldev/atsushieno/ktmidi/ci/ClientConnection;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;ZILjava/lang/Object;)V - public final fun sendSubscribeProperty (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public static synthetic fun sendSubscribeProperty$default (Ldev/atsushieno/ktmidi/ci/ClientConnection;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V - public final fun sendUnsubscribeProperty (Ljava/lang/String;)V public final fun setDeviceInfo (Ldev/atsushieno/ktmidi/ci/MidiCIDeviceInfo;)V public final fun setMaxSimultaneousPropertyRequests (B)V public final fun setProductInstanceId (Ljava/lang/String;)V - public final fun setPropertyClient (Ldev/atsushieno/ktmidi/ci/MidiCIClientPropertyRules;)V -} - -public final class dev/atsushieno/ktmidi/ci/ClientConnection$ProfileClient { - public fun (Ldev/atsushieno/ktmidi/ci/ClientConnection;)V - public final fun getDevice ()Ldev/atsushieno/ktmidi/ci/MidiCIDevice; - public final fun getProfiles ()Ldev/atsushieno/ktmidi/ci/ObservableProfileList; - public final fun processProfileAddedReport (Ldev/atsushieno/ktmidi/ci/Message$ProfileAdded;)V - public final fun processProfileDetailsReply (Ldev/atsushieno/ktmidi/ci/Message$ProfileDetailsReply;)V - public final fun processProfileDisabledReport (Ldev/atsushieno/ktmidi/ci/Message$ProfileDisabled;)V - public final fun processProfileEnabledReport (Ldev/atsushieno/ktmidi/ci/Message$ProfileEnabled;)V - public final fun processProfileRemovedReport (Ldev/atsushieno/ktmidi/ci/Message$ProfileRemoved;)V - public final fun processProfileReply (Ldev/atsushieno/ktmidi/ci/Message$ProfileReply;)V - public final fun setProfile (BBLdev/atsushieno/ktmidi/ci/MidiCIProfileId;ZS)V + public final fun setPropertyRules (Ldev/atsushieno/ktmidi/ci/MidiCIClientPropertyRules;)V } public final class dev/atsushieno/ktmidi/ci/ClientObservablePropertyList : dev/atsushieno/ktmidi/ci/ObservablePropertyList { @@ -1074,6 +1045,12 @@ public final class dev/atsushieno/ktmidi/ci/ObservablePropertyListKt { public static final fun getMediaTypes (Ldev/atsushieno/ktmidi/ci/PropertyMetadata;)Ljava/util/List; } +public final class dev/atsushieno/ktmidi/ci/ProfileClient { + public fun (Ldev/atsushieno/ktmidi/ci/MidiCIDevice;Ldev/atsushieno/ktmidi/ci/ClientConnection;)V + public final fun getProfiles ()Ldev/atsushieno/ktmidi/ci/ObservableProfileList; + public final fun setProfile (BBLdev/atsushieno/ktmidi/ci/MidiCIProfileId;ZS)V +} + public final class dev/atsushieno/ktmidi/ci/ProfileConfigurationHostFacade { public fun (Ldev/atsushieno/ktmidi/ci/MidiCIDevice;)V public final fun addProfile (Ldev/atsushieno/ktmidi/ci/MidiCIProfile;)V @@ -1112,6 +1089,23 @@ public final class dev/atsushieno/ktmidi/ci/PropertyChunkManager$Chunk { public fun toString ()Ljava/lang/String; } +public final class dev/atsushieno/ktmidi/ci/PropertyClient { + public fun (Ldev/atsushieno/ktmidi/ci/MidiCIDevice;Ldev/atsushieno/ktmidi/ci/ClientConnection;)V + public final fun getPendingChunkManager ()Ldev/atsushieno/ktmidi/ci/PropertyChunkManager; + public final fun getProperties ()Ldev/atsushieno/ktmidi/ci/ClientObservablePropertyList; + public final fun getSubscriptionUpdated ()Ljava/util/List; + public final fun getSubscriptions ()Ljava/util/List; + public final fun sendGetPropertyData (Ldev/atsushieno/ktmidi/ci/Message$GetPropertyData;)V + public final fun sendGetPropertyData (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;)V + public static synthetic fun sendGetPropertyData$default (Ldev/atsushieno/ktmidi/ci/PropertyClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)V + public final fun sendSetPropertyData (Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Z)V + public final fun sendSetPropertyData (Ljava/util/List;Ljava/util/List;)V + public static synthetic fun sendSetPropertyData$default (Ldev/atsushieno/ktmidi/ci/PropertyClient;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;ZILjava/lang/Object;)V + public final fun sendSubscribeProperty (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public static synthetic fun sendSubscribeProperty$default (Ldev/atsushieno/ktmidi/ci/PropertyClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V + public final fun sendUnsubscribeProperty (Ljava/lang/String;)V +} + public final class dev/atsushieno/ktmidi/ci/PropertyExchangeHostFacade { public fun (Ldev/atsushieno/ktmidi/ci/MidiCIDevice;)V public final fun addProperty (Ldev/atsushieno/ktmidi/ci/PropertyMetadata;)V diff --git a/ktmidi-ci/api/jvm/ktmidi-ci.api b/ktmidi-ci/api/jvm/ktmidi-ci.api index b362ffee0..e272c84a6 100644 --- a/ktmidi-ci/api/jvm/ktmidi-ci.api +++ b/ktmidi-ci/api/jvm/ktmidi-ci.api @@ -117,44 +117,15 @@ public final class dev/atsushieno/ktmidi/ci/ClientConnection { public synthetic fun (Ldev/atsushieno/ktmidi/ci/MidiCIDevice;ILdev/atsushieno/ktmidi/ci/DeviceDetails;BLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getDeviceInfo ()Ldev/atsushieno/ktmidi/ci/MidiCIDeviceInfo; public final fun getMaxSimultaneousPropertyRequests ()B - public final fun getPendingChunkManager ()Ldev/atsushieno/ktmidi/ci/PropertyChunkManager; public final fun getProductInstanceId ()Ljava/lang/String; - public final fun getProfileClient ()Ldev/atsushieno/ktmidi/ci/ClientConnection$ProfileClient; - public final fun getProperties ()Ldev/atsushieno/ktmidi/ci/ClientObservablePropertyList; - public final fun getPropertyClient ()Ldev/atsushieno/ktmidi/ci/MidiCIClientPropertyRules; - public final fun getSubscriptionUpdated ()Ljava/util/List; - public final fun getSubscriptions ()Ljava/util/List; + public final fun getProfileClient ()Ldev/atsushieno/ktmidi/ci/ProfileClient; + public final fun getPropertyClient ()Ldev/atsushieno/ktmidi/ci/PropertyClient; + public final fun getPropertyRules ()Ldev/atsushieno/ktmidi/ci/MidiCIClientPropertyRules; public final fun getTargetMUID ()I - public final fun processGetDataReply (Ldev/atsushieno/ktmidi/ci/Message$GetPropertyDataReply;)V - public final fun processPropertyCapabilitiesReply (Ldev/atsushieno/ktmidi/ci/Message$PropertyGetCapabilitiesReply;)V - public final fun processPropertySubscriptionReply (Ldev/atsushieno/ktmidi/ci/Message$SubscribePropertyReply;)V - public final fun processSubscribeProperty (Ldev/atsushieno/ktmidi/ci/Message$SubscribeProperty;)V - public final fun sendGetPropertyData (Ldev/atsushieno/ktmidi/ci/Message$GetPropertyData;)V - public final fun sendGetPropertyData (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;)V - public static synthetic fun sendGetPropertyData$default (Ldev/atsushieno/ktmidi/ci/ClientConnection;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)V - public final fun sendSetPropertyData (Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Z)V - public final fun sendSetPropertyData (Ljava/util/List;Ljava/util/List;)V - public static synthetic fun sendSetPropertyData$default (Ldev/atsushieno/ktmidi/ci/ClientConnection;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;ZILjava/lang/Object;)V - public final fun sendSubscribeProperty (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public static synthetic fun sendSubscribeProperty$default (Ldev/atsushieno/ktmidi/ci/ClientConnection;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V - public final fun sendUnsubscribeProperty (Ljava/lang/String;)V public final fun setDeviceInfo (Ldev/atsushieno/ktmidi/ci/MidiCIDeviceInfo;)V public final fun setMaxSimultaneousPropertyRequests (B)V public final fun setProductInstanceId (Ljava/lang/String;)V - public final fun setPropertyClient (Ldev/atsushieno/ktmidi/ci/MidiCIClientPropertyRules;)V -} - -public final class dev/atsushieno/ktmidi/ci/ClientConnection$ProfileClient { - public fun (Ldev/atsushieno/ktmidi/ci/ClientConnection;)V - public final fun getDevice ()Ldev/atsushieno/ktmidi/ci/MidiCIDevice; - public final fun getProfiles ()Ldev/atsushieno/ktmidi/ci/ObservableProfileList; - public final fun processProfileAddedReport (Ldev/atsushieno/ktmidi/ci/Message$ProfileAdded;)V - public final fun processProfileDetailsReply (Ldev/atsushieno/ktmidi/ci/Message$ProfileDetailsReply;)V - public final fun processProfileDisabledReport (Ldev/atsushieno/ktmidi/ci/Message$ProfileDisabled;)V - public final fun processProfileEnabledReport (Ldev/atsushieno/ktmidi/ci/Message$ProfileEnabled;)V - public final fun processProfileRemovedReport (Ldev/atsushieno/ktmidi/ci/Message$ProfileRemoved;)V - public final fun processProfileReply (Ldev/atsushieno/ktmidi/ci/Message$ProfileReply;)V - public final fun setProfile (BBLdev/atsushieno/ktmidi/ci/MidiCIProfileId;ZS)V + public final fun setPropertyRules (Ldev/atsushieno/ktmidi/ci/MidiCIClientPropertyRules;)V } public final class dev/atsushieno/ktmidi/ci/ClientObservablePropertyList : dev/atsushieno/ktmidi/ci/ObservablePropertyList { @@ -1074,6 +1045,12 @@ public final class dev/atsushieno/ktmidi/ci/ObservablePropertyListKt { public static final fun getMediaTypes (Ldev/atsushieno/ktmidi/ci/PropertyMetadata;)Ljava/util/List; } +public final class dev/atsushieno/ktmidi/ci/ProfileClient { + public fun (Ldev/atsushieno/ktmidi/ci/MidiCIDevice;Ldev/atsushieno/ktmidi/ci/ClientConnection;)V + public final fun getProfiles ()Ldev/atsushieno/ktmidi/ci/ObservableProfileList; + public final fun setProfile (BBLdev/atsushieno/ktmidi/ci/MidiCIProfileId;ZS)V +} + public final class dev/atsushieno/ktmidi/ci/ProfileConfigurationHostFacade { public fun (Ldev/atsushieno/ktmidi/ci/MidiCIDevice;)V public final fun addProfile (Ldev/atsushieno/ktmidi/ci/MidiCIProfile;)V @@ -1112,6 +1089,23 @@ public final class dev/atsushieno/ktmidi/ci/PropertyChunkManager$Chunk { public fun toString ()Ljava/lang/String; } +public final class dev/atsushieno/ktmidi/ci/PropertyClient { + public fun (Ldev/atsushieno/ktmidi/ci/MidiCIDevice;Ldev/atsushieno/ktmidi/ci/ClientConnection;)V + public final fun getPendingChunkManager ()Ldev/atsushieno/ktmidi/ci/PropertyChunkManager; + public final fun getProperties ()Ldev/atsushieno/ktmidi/ci/ClientObservablePropertyList; + public final fun getSubscriptionUpdated ()Ljava/util/List; + public final fun getSubscriptions ()Ljava/util/List; + public final fun sendGetPropertyData (Ldev/atsushieno/ktmidi/ci/Message$GetPropertyData;)V + public final fun sendGetPropertyData (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;)V + public static synthetic fun sendGetPropertyData$default (Ldev/atsushieno/ktmidi/ci/PropertyClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)V + public final fun sendSetPropertyData (Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Z)V + public final fun sendSetPropertyData (Ljava/util/List;Ljava/util/List;)V + public static synthetic fun sendSetPropertyData$default (Ldev/atsushieno/ktmidi/ci/PropertyClient;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;ZILjava/lang/Object;)V + public final fun sendSubscribeProperty (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public static synthetic fun sendSubscribeProperty$default (Ldev/atsushieno/ktmidi/ci/PropertyClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V + public final fun sendUnsubscribeProperty (Ljava/lang/String;)V +} + public final class dev/atsushieno/ktmidi/ci/PropertyExchangeHostFacade { public fun (Ldev/atsushieno/ktmidi/ci/MidiCIDevice;)V public final fun addProperty (Ldev/atsushieno/ktmidi/ci/PropertyMetadata;)V diff --git a/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/ClientConnection.kt b/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/ClientConnection.kt index 0a5bf01c0..e5d123e9f 100644 --- a/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/ClientConnection.kt +++ b/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/ClientConnection.kt @@ -17,78 +17,92 @@ class ClientConnection( var maxSimultaneousPropertyRequests: Byte = 0, var productInstanceId: String = "", ) { - var propertyClient: MidiCIClientPropertyRules = CommonRulesPropertyClient(parent, this) + var propertyRules: MidiCIClientPropertyRules = CommonRulesPropertyClient(parent, this) var deviceInfo = MidiCIDeviceInfo( - deviceDetails.manufacturer, deviceDetails.family, deviceDetails.modelNumber, deviceDetails.softwareRevisionLevel, - "", "", "", "", "") - - // This is going to be the entry point for all the profile client features foe MidiCIDevice. - class ProfileClient(private val conn: ClientConnection) { - val device by conn::parent - - val profiles = ObservableProfileList(mutableListOf()) - - fun setProfile( - group: Byte, - address: Byte, - profile: MidiCIProfileId, - enabled: Boolean, - numChannelsRequested: Short - ) { - val common = Message.Common(device.muid, conn.targetMUID, address, group) - if (enabled) { - val msg = Message.SetProfileOn( - common, profile, - // NOTE: juce_midi_ci has a bug that it expects 1 for 7E and 7F, whereas MIDI-CI v1.2 states: - // "When the Profile Destination field is set to address 0x7E or 0x7F, the number of Channels is determined - // by the width of the Group or Function Block. Set the Number of Channels Requested field to a value of 0x0000." - if (address < 0x10 || ImplementationSettings.workaroundJUCEProfileNumChannelsIssue) { - if (numChannelsRequested < 1) 1 else numChannelsRequested - } else numChannelsRequested - ) - device.messenger.send(msg) - } else { - val msg = Message.SetProfileOff(common, profile) - device.messenger.send(msg) - } - } + deviceDetails.manufacturer, + deviceDetails.family, + deviceDetails.modelNumber, + deviceDetails.softwareRevisionLevel, + "", + "", + "", + "", + "" + ) + + val profileClient = ProfileClient(parent, this) + + val propertyClient = PropertyClient(parent, this) +} - fun processProfileReply(msg: Message.ProfileReply) { - msg.enabledProfiles.forEach { - profiles.add(MidiCIProfile(it, msg.group, msg.address, true, if (msg.address >= 0x7E) 0 else 1)) - } - msg.disabledProfiles.forEach { - profiles.add(MidiCIProfile(it, msg.group, msg.address, false, if (msg.address >= 0x7E) 0 else 1)) - } +// This is going to be the entry point for all the profile client features foe MidiCIDevice. +class ProfileClient(private val device: MidiCIDevice, private val conn: ClientConnection) { + + val profiles = ObservableProfileList(mutableListOf()) + + fun setProfile( + group: Byte, + address: Byte, + profile: MidiCIProfileId, + enabled: Boolean, + numChannelsRequested: Short + ) { + val common = Message.Common(device.muid, conn.targetMUID, address, group) + if (enabled) { + val msg = Message.SetProfileOn( + common, profile, + // NOTE: juce_midi_ci has a bug that it expects 1 for 7E and 7F, whereas MIDI-CI v1.2 states: + // "When the Profile Destination field is set to address 0x7E or 0x7F, the number of Channels is determined + // by the width of the Group or Function Block. Set the Number of Channels Requested field to a value of 0x0000." + if (address < 0x10 || ImplementationSettings.workaroundJUCEProfileNumChannelsIssue) { + if (numChannelsRequested < 1) 1 else numChannelsRequested + } else numChannelsRequested + ) + device.messenger.send(msg) + } else { + val msg = Message.SetProfileOff(common, profile) + device.messenger.send(msg) } + } - fun processProfileAddedReport(msg: Message.ProfileAdded) { - profiles.add(MidiCIProfile(msg.profile, msg.group, msg.address, false, if (msg.address >= 0x7E) 0 else 1)) + internal fun processProfileReply(msg: Message.ProfileReply) { + msg.enabledProfiles.forEach { + profiles.add(MidiCIProfile(it, msg.group, msg.address, true, if (msg.address >= 0x7E) 0 else 1)) } - - fun processProfileRemovedReport(msg: Message.ProfileRemoved) { - profiles.remove(MidiCIProfile(msg.profile, msg.group, msg.address, false, 0)) + msg.disabledProfiles.forEach { + profiles.add(MidiCIProfile(it, msg.group, msg.address, false, if (msg.address >= 0x7E) 0 else 1)) } + } - fun processProfileEnabledReport(msg: Message.ProfileEnabled) { - profiles.setEnabled(true, msg.address, msg.profile, msg.numChannelsEnabled) - } + internal fun processProfileAddedReport(msg: Message.ProfileAdded) { + profiles.add(MidiCIProfile(msg.profile, msg.group, msg.address, false, if (msg.address >= 0x7E) 0 else 1)) + } - fun processProfileDisabledReport(msg: Message.ProfileDisabled) { - profiles.setEnabled(false, msg.address, msg.profile, msg.numChannelsDisabled) - } + internal fun processProfileRemovedReport(msg: Message.ProfileRemoved) { + profiles.remove(MidiCIProfile(msg.profile, msg.group, msg.address, false, 0)) + } - fun processProfileDetailsReply(msg: Message.ProfileDetailsReply) { - // nothing to perform so far - use events if you need anything further - } + internal fun processProfileEnabledReport(msg: Message.ProfileEnabled) { + profiles.setEnabled(true, msg.address, msg.profile, msg.numChannelsEnabled) } - val profileClient = ProfileClient(this) + internal fun processProfileDisabledReport(msg: Message.ProfileDisabled) { + profiles.setEnabled(false, msg.address, msg.profile, msg.numChannelsDisabled) + } + + internal fun processProfileDetailsReply(msg: Message.ProfileDetailsReply) { + // nothing to perform so far - use events if you need anything further + } +} - // FIXME: isolate them into another class. - // It should be `PropertyClient`, but the name conflicts with the constructor argument right now...! +class PropertyClient(private val device: MidiCIDevice, private val conn: ClientConnection) { + private val muid by device::muid + private val logger by device::logger + private val messenger by device::messenger + private val targetMUID by conn::targetMUID + private val propertyRules by conn::propertyRules - val properties = ClientObservablePropertyList(parent.logger, propertyClient) + val properties = ClientObservablePropertyList(logger, propertyRules) private val openRequests = mutableListOf() val subscriptions = mutableListOf() @@ -101,23 +115,23 @@ class ClientConnection( return Pair( command, Message.SubscribePropertyReply( - Message.Common(parent.muid, msg.sourceMUID, msg.address, msg.group), + Message.Common(muid, msg.sourceMUID, msg.address, msg.group), msg.requestId, - propertyClient.createStatusHeader(PropertyExchangeStatus.OK), listOf() + propertyRules.createStatusHeader(PropertyExchangeStatus.OK), listOf() ) ) } private fun handleUnsubscriptionNotification(ourMUID: Int, msg: Message.SubscribeProperty): Pair { - val sub = subscriptions.firstOrNull { it.subscriptionId == propertyClient.getHeaderFieldString(msg.header, PropertyCommonHeaderKeys.SUBSCRIBE_ID) } ?: - subscriptions.firstOrNull { it.propertyId == propertyClient.getPropertyIdForHeader(msg.header) } + val sub = subscriptions.firstOrNull { it.subscriptionId == propertyRules.getHeaderFieldString(msg.header, PropertyCommonHeaderKeys.SUBSCRIBE_ID) } ?: + subscriptions.firstOrNull { it.propertyId == propertyRules.getPropertyIdForHeader(msg.header) } if (sub == null) return Pair("subscription ID is not specified in the unsubscription request", null) subscriptions.remove(sub) return Pair(null, Message.SubscribePropertyReply( Message.Common(ourMUID, msg.sourceMUID, msg.address, msg.group), msg.requestId, - propertyClient.createStatusHeader(PropertyExchangeStatus.OK), listOf() + propertyRules.createStatusHeader(PropertyExchangeStatus.OK), listOf() )) } @@ -135,11 +149,11 @@ class ClientConnection( private fun promoteSubscriptionAsUnsubscribing(propertyId: String, newRequestId: Byte) { val sub = subscriptions.firstOrNull { it.propertyId == propertyId } if (sub == null) { - parent.logger.logError("Cannot unsubscribe property as not found: $propertyId") + logger.logError("Cannot unsubscribe property as not found: $propertyId") return } if (sub.state == SubscriptionActionState.Unsubscribing) { - parent.logger.logError("Unsubscription for the property is already underway (property: ${sub.propertyId}, subscriptionId: ${sub.subscriptionId}, state: ${sub.state})") + logger.logError("Unsubscription for the property is already underway (property: ${sub.propertyId}, subscriptionId: ${sub.subscriptionId}, state: ${sub.state})") return } sub.pendingRequestId = newRequestId @@ -147,20 +161,20 @@ class ClientConnection( subscriptionUpdated.forEach { it(sub) } } - fun processPropertySubscriptionReply(msg: Message.SubscribePropertyReply) { - if (propertyClient.getHeaderFieldInteger(msg.header, PropertyCommonHeaderKeys.STATUS) != PropertyExchangeStatus.OK) + internal fun processPropertySubscriptionReply(msg: Message.SubscribePropertyReply) { + if (propertyRules.getHeaderFieldInteger(msg.header, PropertyCommonHeaderKeys.STATUS) != PropertyExchangeStatus.OK) return // FIXME: should we do anything further here? - val subscriptionId = propertyClient.getHeaderFieldString(msg.header, PropertyCommonHeaderKeys.SUBSCRIBE_ID) + val subscriptionId = propertyRules.getHeaderFieldString(msg.header, PropertyCommonHeaderKeys.SUBSCRIBE_ID) val sub = subscriptions.firstOrNull { subscriptionId == it.subscriptionId } ?: subscriptions.firstOrNull { it.pendingRequestId == msg.requestId } if (sub == null) { - parent.logger.logError("There was no pending subscription that matches subscribeId ($subscriptionId) or requestId (${msg.requestId})") + logger.logError("There was no pending subscription that matches subscribeId ($subscriptionId) or requestId (${msg.requestId})") return } // `subscribeId` must be attached everywhere, except for unsubscription reply. if (subscriptionId == null && sub.state != SubscriptionActionState.Unsubscribing) { - parent.logger.logError("Subscription ID is missing in the Reply to Subscription message. requestId: ${msg.requestId}") + logger.logError("Subscription ID is missing in the Reply to Subscription message. requestId: ${msg.requestId}") if (!ImplementationSettings.workaroundJUCEMissingSubscriptionIdIssue) return } @@ -168,7 +182,7 @@ class ClientConnection( when (sub.state) { SubscriptionActionState.Subscribed, SubscriptionActionState.Unsubscribed -> { - parent.logger.logError("Received Subscription Reply, but it is unexpected (existing subscription: property = ${sub.propertyId}, subscriptionId = ${sub.subscriptionId}, state = ${sub.state})") + logger.logError("Received Subscription Reply, but it is unexpected (existing subscription: property = ${sub.propertyId}, subscriptionId = ${sub.subscriptionId}, state = ${sub.state})") return } else -> {} @@ -176,7 +190,7 @@ class ClientConnection( sub.subscriptionId = subscriptionId - propertyClient.processPropertySubscriptionResult(sub, msg) + propertyRules.processPropertySubscriptionResult(sub, msg) if (sub.state == SubscriptionActionState.Unsubscribing) { // do unsubscribe @@ -191,89 +205,89 @@ class ClientConnection( // It is Common Rules specific fun sendGetPropertyData(resource: String, encoding: String? = null, paginateOffset: Int? = null, paginateLimit: Int? = null) { - val header = propertyClient.createDataRequestHeader(resource, mapOf( + val header = propertyRules.createDataRequestHeader(resource, mapOf( PropertyCommonHeaderKeys.MUTUAL_ENCODING to encoding, PropertyCommonHeaderKeys.SET_PARTIAL to false, PropertyCommonHeaderKeys.OFFSET to paginateOffset, PropertyCommonHeaderKeys.LIMIT to paginateLimit ).filter { it.value != null }) - val msg = Message.GetPropertyData(Message.Common(parent.muid, targetMUID, MidiCIConstants.ADDRESS_FUNCTION_BLOCK, parent.config.group), - parent.messenger.requestIdSerial++, header) + val msg = Message.GetPropertyData(Message.Common(muid, targetMUID, MidiCIConstants.ADDRESS_FUNCTION_BLOCK, device.config.group), + messenger.requestIdSerial++, header) sendGetPropertyData(msg) } // unlike the other overload, it is not specific to Common Rules for PE fun sendGetPropertyData(msg: Message.GetPropertyData) { openRequests.add(msg) - parent.messenger.send(msg) + messenger.send(msg) } // It is Common Rules specific fun sendSetPropertyData(resource: String, data: List, encoding: String? = null, isPartial: Boolean = false) { - val header = propertyClient.createDataRequestHeader(resource, mapOf( + val header = propertyRules.createDataRequestHeader(resource, mapOf( PropertyCommonHeaderKeys.MUTUAL_ENCODING to encoding, PropertyCommonHeaderKeys.SET_PARTIAL to isPartial)) - val encodedBody = propertyClient.encodeBody(data, encoding) + val encodedBody = propertyRules.encodeBody(data, encoding) sendSetPropertyData(header, encodedBody) } // unlike the other overload, it is not specific to Common Rules for PE fun sendSetPropertyData(header: List, body: List) = - parent.messenger.send(Message.SetPropertyData(Message.Common(parent.muid, targetMUID, MidiCIConstants.ADDRESS_FUNCTION_BLOCK, parent.config.group), - parent.messenger.requestIdSerial++, header, body)) + messenger.send(Message.SetPropertyData(Message.Common(muid, targetMUID, MidiCIConstants.ADDRESS_FUNCTION_BLOCK, device.config.group), + messenger.requestIdSerial++, header, body)) fun sendSubscribeProperty(resource: String, mutualEncoding: String? = null, subscriptionId: String? = null) { - val header = propertyClient.createSubscriptionHeader(resource, mapOf( + val header = propertyRules.createSubscriptionHeader(resource, mapOf( PropertyCommonHeaderKeys.COMMAND to MidiCISubscriptionCommand.START, PropertyCommonHeaderKeys.MUTUAL_ENCODING to mutualEncoding)) - val msg = Message.SubscribeProperty(Message.Common(parent.muid, targetMUID, MidiCIConstants.ADDRESS_FUNCTION_BLOCK, parent.config.group), - parent.messenger.requestIdSerial++, header, listOf()) + val msg = Message.SubscribeProperty(Message.Common(muid, targetMUID, MidiCIConstants.ADDRESS_FUNCTION_BLOCK, device.config.group), + messenger.requestIdSerial++, header, listOf()) addPendingSubscription(msg.requestId, subscriptionId, resource) - parent.messenger.send(msg) + messenger.send(msg) } fun sendUnsubscribeProperty(propertyId: String) { - val newRequestId = parent.messenger.requestIdSerial++ - val header = propertyClient.createSubscriptionHeader(propertyId, mapOf( + val newRequestId = messenger.requestIdSerial++ + val header = propertyRules.createSubscriptionHeader(propertyId, mapOf( PropertyCommonHeaderKeys.COMMAND to MidiCISubscriptionCommand.END, PropertyCommonHeaderKeys.SUBSCRIBE_ID to subscriptions.firstOrNull { it.propertyId == propertyId}?.subscriptionId)) - val msg = Message.SubscribeProperty(Message.Common(parent.muid, targetMUID, MidiCIConstants.ADDRESS_FUNCTION_BLOCK, parent.config.group), + val msg = Message.SubscribeProperty(Message.Common(muid, targetMUID, MidiCIConstants.ADDRESS_FUNCTION_BLOCK, device.config.group), newRequestId, header, listOf()) promoteSubscriptionAsUnsubscribing(propertyId, newRequestId) - parent.messenger.send(msg) + messenger.send(msg) } // client PE event receivers - fun processPropertyCapabilitiesReply(msg: Message.PropertyGetCapabilitiesReply) { - maxSimultaneousPropertyRequests = msg.maxSimultaneousRequests + internal fun processPropertyCapabilitiesReply(msg: Message.PropertyGetCapabilitiesReply) { + conn.maxSimultaneousPropertyRequests = msg.maxSimultaneousRequests // proceed to query resource list - if (parent.config.autoSendGetResourceList) - propertyClient.requestPropertyList(msg.group) + if (device.config.autoSendGetResourceList) + propertyRules.requestPropertyList(msg.group) } - fun processGetDataReply(msg: Message.GetPropertyDataReply) { + internal fun processGetDataReply(msg: Message.GetPropertyDataReply) { val req = openRequests.firstOrNull { it.requestId == msg.requestId } ?: return openRequests.remove(req) - val status = propertyClient.getHeaderFieldInteger(msg.header, PropertyCommonHeaderKeys.STATUS) + val status = propertyRules.getHeaderFieldInteger(msg.header, PropertyCommonHeaderKeys.STATUS) ?: return if (status == PropertyExchangeStatus.OK) { - val propertyId = propertyClient.getPropertyIdForHeader(req.header) + val propertyId = propertyRules.getPropertyIdForHeader(req.header) properties.updateValue(propertyId, msg) - propertyClient.propertyValueUpdated(propertyId, msg.body) + propertyRules.propertyValueUpdated(propertyId, msg.body) } } - fun processSubscribeProperty(msg: Message.SubscribeProperty) { - val reply = when (propertyClient.getHeaderFieldString(msg.header, PropertyCommonHeaderKeys.COMMAND)) { - MidiCISubscriptionCommand.END -> handleUnsubscriptionNotification(parent.muid, msg) + internal fun processSubscribeProperty(msg: Message.SubscribeProperty) { + val reply = when (propertyRules.getHeaderFieldString(msg.header, PropertyCommonHeaderKeys.COMMAND)) { + MidiCISubscriptionCommand.END -> handleUnsubscriptionNotification(muid, msg) else -> updatePropertyBySubscribe(msg) } if (reply.second != null) - parent.messenger.send(reply.second!!) + messenger.send(reply.second!!) // If the update was NOTIFY, then it is supposed to send Get Data request. if (reply.first == MidiCISubscriptionCommand.NOTIFY) - sendGetPropertyData(propertyClient.getPropertyIdForHeader(msg.header)) + sendGetPropertyData(propertyRules.getPropertyIdForHeader(msg.header)) } } \ No newline at end of file diff --git a/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/Messenger.kt b/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/Messenger.kt index a0e5a6ba8..007eba131 100644 --- a/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/Messenger.kt +++ b/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/Messenger.kt @@ -222,31 +222,32 @@ class Messenger( var processProfileReply = { msg: Message.ProfileReply -> messageReceived.forEach { it(msg) } - continueOnClient(msg, CISubId2.PROFILE_INQUIRY_REPLY) { it.profileClient.processProfileReply(msg) } + onClient(msg, CISubId2.PROFILE_INQUIRY_REPLY) { profileClient.processProfileReply(msg) } } var processProfileAddedReport = { msg: Message.ProfileAdded -> messageReceived.forEach { it(msg) } - continueOnClient(msg, CISubId2.PROFILE_ADDED_REPORT) { it.profileClient.processProfileAddedReport(msg) } + onClient(msg, CISubId2.PROFILE_ADDED_REPORT) { profileClient.processProfileAddedReport(msg) } } var processProfileRemovedReport: (msg: Message.ProfileRemoved) -> Unit = { msg -> messageReceived.forEach { it(msg) } - continueOnClient(msg, CISubId2.PROFILE_REMOVED_REPORT) { it.profileClient.processProfileRemovedReport(msg) } + onClient(msg, CISubId2.PROFILE_REMOVED_REPORT) { profileClient.processProfileRemovedReport(msg) } } var processProfileEnabledReport: (msg: Message.ProfileEnabled) -> Unit = { msg -> messageReceived.forEach { it(msg) } - continueOnClient(msg, CISubId2.PROFILE_ENABLED_REPORT) { it.profileClient.processProfileEnabledReport(msg) } + onClient(msg, CISubId2.PROFILE_ENABLED_REPORT) { profileClient.processProfileEnabledReport(msg) } } + var processProfileDisabledReport: (msg: Message.ProfileDisabled) -> Unit = { msg -> messageReceived.forEach { it(msg) } - continueOnClient(msg, CISubId2.PROFILE_DISABLED_REPORT) { it.profileClient.processProfileDisabledReport(msg) } + onClient(msg, CISubId2.PROFILE_DISABLED_REPORT) { profileClient.processProfileDisabledReport(msg) } } var processProfileDetailsReply: (msg: Message.ProfileDetailsReply) -> Unit = { msg -> messageReceived.forEach { it(msg) } - continueOnClient(msg, CISubId2.PROFILE_DETAILS_REPLY) { it.profileClient.processProfileDetailsReply(msg) } + onClient(msg, CISubId2.PROFILE_DETAILS_REPLY) { profileClient.processProfileDetailsReply(msg) } } // Local Profile Configuration @@ -360,23 +361,14 @@ class Messenger( // Local - private fun conn(msg: Message, subId2: Byte): ClientConnection? { - val conn = connections[msg.sourceMUID] - if (conn != null) - return conn - // Unknown MUID - send back NAK - sendNakForUnknownMUID(Message.Common(muid, msg.sourceMUID, msg.address, msg.group), subId2) - return null - } - var processPropertyCapabilitiesReply: (msg: Message.PropertyGetCapabilitiesReply) -> Unit = { msg -> device.messageReceived.forEach { it(msg) } - conn(msg, CISubId2.PROPERTY_CAPABILITIES_REPLY)?.processPropertyCapabilitiesReply(msg) + onClient(msg, CISubId2.PROPERTY_CAPABILITIES_REPLY) { propertyClient.processPropertyCapabilitiesReply(msg) } } var processGetDataReply: (msg: Message.GetPropertyDataReply) -> Unit = { msg -> messageReceived.forEach { it(msg) } - conn(msg, CISubId2.PROPERTY_GET_DATA_REPLY)?.processGetDataReply(msg) + onClient(msg, CISubId2.PROPERTY_GET_DATA_REPLY) { propertyClient.processGetDataReply(msg) } } var processSetDataReply: (msg: Message.SetPropertyDataReply) -> Unit = { msg -> @@ -393,11 +385,11 @@ class Messenger( MidiCISubscriptionCommand.FULL, MidiCISubscriptionCommand.PARTIAL, MidiCISubscriptionCommand.NOTIFY -> - conn(msg, CISubId2.PROPERTY_SUBSCRIBE)?.processSubscribeProperty(msg) // for a subscriber + onClient(msg, CISubId2.PROPERTY_SUBSCRIBE) { propertyClient.processSubscribeProperty(msg) } // for a subscriber MidiCISubscriptionCommand.END -> { // We need to identify whether it is sent by the notifier or one of the subscribers if (connections[msg.sourceMUID] != null) - conn(msg, CISubId2.PROPERTY_SUBSCRIBE)?.processSubscribeProperty(msg) // for a subscriber + onClient(msg, CISubId2.PROPERTY_SUBSCRIBE) { propertyClient.processSubscribeProperty(msg) } // for a subscriber else device.propertyHost.processSubscribeProperty(msg) // for the notifier } @@ -413,7 +405,7 @@ class Messenger( // (whether it is in our listening subscriptions xor it is request to unsubscribe from client) // We need to identify whether it is sent by the notifier or one of the subscribers if (connections[msg.sourceMUID] != null) - conn(msg, CISubId2.PROPERTY_SUBSCRIBE_REPLY)?.processPropertySubscriptionReply(msg) // for a subscriber + onClient(msg, CISubId2.PROPERTY_SUBSCRIBE_REPLY) { propertyClient.processPropertySubscriptionReply(msg) } // for a subscriber // else -> nothing to do in particular by default } @@ -527,12 +519,13 @@ class Messenger( private val emptyNakDetails = List(5) {0} - private fun continueOnClient(msg: T, subId2: Byte, func: (conn: ClientConnection)->Unit) where T: Message { + private fun onClient(msg: Message, subId2: Byte, func: ClientConnection.() -> Unit) { val conn = connections[msg.sourceMUID] if (conn != null) - func(conn) + conn.func() else - sendNakForError(getResponseCommonForInput(msg.common), subId2, CINakStatus.Nak, 0, emptyNakDetails, "Profile Reply from unknown MUID") + // Unknown MUID - send back NAK + sendNakForUnknownMUID(Message.Common(muid, msg.sourceMUID, msg.address, msg.group), subId2) } private fun getResponseCommonForInput(common: Message.Common) = @@ -573,7 +566,7 @@ class Messenger( private fun handleChunk(common: Message.Common, requestId: Byte, chunkIndex: Short, numChunks: Short, header: List, body: List, onComplete: (header: List, body: List) -> Unit) { - val pendingChunkManager = connections[common.sourceMUID]?.pendingChunkManager ?: localPendingChunkManager + val pendingChunkManager = connections[common.sourceMUID]?.propertyClient?.pendingChunkManager ?: localPendingChunkManager if (chunkIndex < numChunks) { pendingChunkManager.addPendingChunk(Clock.System.now().epochSeconds, common.sourceMUID, requestId, header, body) } else { diff --git a/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/propertycommonrules/PropertyCommonRules.Client.kt b/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/propertycommonrules/PropertyCommonRules.Client.kt index 6c34611f8..89fb04bd2 100644 --- a/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/propertycommonrules/PropertyCommonRules.Client.kt +++ b/ktmidi-ci/src/commonMain/kotlin/dev/atsushieno/ktmidi/ci/propertycommonrules/PropertyCommonRules.Client.kt @@ -41,7 +41,7 @@ class CommonRulesPropertyClient(private val device: MidiCIDevice, private val co if (device.config.autoSendGetDeviceInfo) { val def = getMetadataList().firstOrNull { it.propertyId == PropertyResourceNames.DEVICE_INFO } as CommonRulesPropertyMetadata? if (def != null) - conn.sendGetPropertyData(PropertyResourceNames.DEVICE_INFO, def.encodings.firstOrNull()) + conn.propertyClient.sendGetPropertyData(PropertyResourceNames.DEVICE_INFO, def.encodings.firstOrNull()) } } // If it is about DeviceInfo, then store the list internally. diff --git a/ktmidi-ci/src/commonTest/kotlin/dev/atsushieno/ktmidi/ci/PropertyExchangeHostFacadeTest.kt b/ktmidi-ci/src/commonTest/kotlin/dev/atsushieno/ktmidi/ci/PropertyExchangeHostFacadeTest.kt index a0c35d483..3dff40262 100644 --- a/ktmidi-ci/src/commonTest/kotlin/dev/atsushieno/ktmidi/ci/PropertyExchangeHostFacadeTest.kt +++ b/ktmidi-ci/src/commonTest/kotlin/dev/atsushieno/ktmidi/ci/PropertyExchangeHostFacadeTest.kt @@ -13,44 +13,47 @@ class PropertyExchangeHostFacadeTest { val mediator = TestCIMediator() val device1 = mediator.device1 val device2 = mediator.device2 + val host = device2.propertyHost val id = "X-01" val prop1 = CommonRulesPropertyMetadata(id).apply { canSet = "partial" canSubscribe = true } - device2.propertyHost.addProperty(prop1) + host.addProperty(prop1) val bytes = Json.serialize(Json.JsonValue("FOO")).toASCIIByteArray().toList() val bytes2 = Json.serialize(Json.JsonValue("BAR")).toASCIIByteArray().toList() - device2.propertyHost.setPropertyValue(id, bytes, false) + host.setPropertyValue(id, bytes, false) device1.sendDiscovery() // test get property val conn = device1.connections[device2.muid] assertNotNull(conn) - conn.sendGetPropertyData(id) - assertContentEquals(bytes, conn.properties.getProperty(id), "getProperty") + val client = conn.propertyClient + + client.sendGetPropertyData(id) + assertContentEquals(bytes, client.properties.getProperty(id), "getProperty") // test set property - conn.sendSetPropertyData(id, bytes2) - assertContentEquals(bytes2, device2.propertyHost.properties.getProperty(id), "getProperty2") + client.sendSetPropertyData(id, bytes2) + assertContentEquals(bytes2, host.properties.getProperty(id), "getProperty2") // subscribe -> update value -> notify - conn.sendSubscribeProperty(id) - assertEquals(1, device2.propertyHost.subscriptions.size, "subscriptions.size after subscription") - device2.propertyHost.setPropertyValue(id, bytes, false) + client.sendSubscribeProperty(id) + assertEquals(1, host.subscriptions.size, "subscriptions.size after subscription") + host.setPropertyValue(id, bytes, false) // it should be reflected on the client side - assertContentEquals(bytes, conn.properties.getProperty(id), "getProperty at client after subscribed property update") - conn.sendUnsubscribeProperty(id) - assertEquals(0, device2.propertyHost.subscriptions.size, "subscriptions.size after unsubscription") + assertContentEquals(bytes, client.properties.getProperty(id), "getProperty at client after subscribed property update") + client.sendUnsubscribeProperty(id) + assertEquals(0, host.subscriptions.size, "subscriptions.size after unsubscription") // subscribe again, but this time unsubscribe from host - conn.sendSubscribeProperty(id) - assertEquals(1, device2.propertyHost.subscriptions.size, "subscriptions.size after subscription, 2nd") - val sub = device2.propertyHost.subscriptions.first() - device2.propertyHost.shutdownSubscription(sub.muid, sub.resource) - assertEquals(0, conn.subscriptions.size, "client subscriptions.size after unsubscription by host") - assertEquals(0, device2.propertyHost.subscriptions.size, "host subscriptions.size after unsubscription by host") + client.sendSubscribeProperty(id) + assertEquals(1, host.subscriptions.size, "subscriptions.size after subscription, 2nd") + val sub = host.subscriptions.first() + host.shutdownSubscription(sub.muid, sub.resource) + assertEquals(0, client.subscriptions.size, "client subscriptions.size after unsubscription by host") + assertEquals(0, host.subscriptions.size, "host subscriptions.size after unsubscription by host") } }