{"id":19966798,"url":"https://github.com/aptpod/iscp-kt","last_synced_at":"2026-03-05T15:35:22.335Z","repository":{"id":193993871,"uuid":"684386286","full_name":"aptpod/iscp-kt","owner":"aptpod","description":"iSCP client library for Java and Kotlin. iSCP (intdash Stream Control Protocol) is an L7 network protocol authored by aptpod, Inc.","archived":false,"fork":false,"pushed_at":"2024-04-19T07:07:47.000Z","size":6016,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-12T08:41:58.774Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aptpod.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-08-29T03:05:37.000Z","updated_at":"2024-04-19T07:03:23.000Z","dependencies_parsed_at":null,"dependency_job_id":"d2217842-92ab-415f-b9c3-90282030ddd8","html_url":"https://github.com/aptpod/iscp-kt","commit_stats":null,"previous_names":["aptpod/iscp-kt"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aptpod%2Fiscp-kt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aptpod%2Fiscp-kt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aptpod%2Fiscp-kt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aptpod%2Fiscp-kt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aptpod","download_url":"https://codeload.github.com/aptpod/iscp-kt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241398946,"owners_count":19956828,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-13T02:38:16.762Z","updated_at":"2026-03-05T15:35:22.325Z","avatar_url":"https://github.com/aptpod.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Module iscp\n\n# iSCP 2.0 Client Library for Kotlin\n\niSCP Client for Kotlin は、iSCP version 2を用いたリアルタイムAPIにアクセスするためのクライアントライブラリです。\n\n## Dependencies\n\n- [Protocol Buffers [Core]](https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java)\n- [Protocol Buffers [Util]](https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util)\n- [Java WebSockets](https://mvnrepository.com/artifact/org.java-websocket/Java-WebSocket)\n\n## Installation\n\n```\n// build.gradle\n...\ndependencies {\n    ...\n    // Install iSCP\n    implementation 'com.aptpod.github:iscp:1.1.0'\n}\n```\n\n```\n// settings.gradle\n...\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n        // Library references for iSCP\n        maven { url \"https://aptpod.github.io/iscp-kt\" }\n    }\n}\n```\n\n## Implementation\n\n### Connect to intdash API\n\nこのサンプルではiscp-ktを使ってintdash APIに接続します。\n\n```kotlin\nimport android.app.Activity\nimport com.aptpod.iscp.connection.Connection\nimport com.aptpod.iscp.connection.ConnectionCallbacks\nimport com.aptpod.iscp.transport.IConnector\nimport com.aptpod.iscp.transport.websocket.WebSocketConnector\n\nclass ExampleActivity : Activity() {\n    /**\n     * 接続するintdashサーバー\n     */\n    var targetServer: String = \"https://example.com\"\n    /**\n     * ノードUUID（ここで指定されたノードとして送受信を行います。\n     *\n     * intdash APIでノードを生成した際に発行されたノードUUIDを指定します。）\n     */\n    var nodeId = \"00000000-0000-0000-0000-000000000000\"\n    /**\n     * アクセストークン\n     *\n     * intdash APIで取得したアクセストークンを指定して下さい。\n     */\n    var accessToken = \"\"\n    /**\n     *  コネクション\n     */\n    var connection: Connection? = null\n\n    fun connect() {\n        // 接続情報のセットアップをします。\n        var urls = targetServer.split(\"://\")\n        var address: String\n        var enableTls: Boolean = false\n        if (urls.size == 1)\n        {\n            address = urls[0]\n        }\n        else\n        {\n            enableTls = urls[0] == \"https\"\n            address = urls[1]\n        }\n        // WebSocketを使って接続するように指定します。\n        var connector: IConnector = WebSocketConnector(enableTls = enableTls)\n        Connection.connectAsync(\n            address = address,\n            connector = connector,\n            tokenSource = {\n                // アクセス用のトークンを指定します。接続時に発生するイベントにより使用されます。\n                // ここでは固定のトークンを返していますが随時トークンの更新を行う実装にするとトークンの期限切れを考える必要がなくなります。\n                accessToken\n            },\n            nodeId = nodeId,\n            completion = { con, ex -\u003e\n                if (con == null) {\n                    // 接続失敗\n                    return@connectAsync\n                }\n                // 接続成功.\n                connection = con\n                con.callbacks = this.connectionCallbacks // ConnectionCallbacks\n                // 以降、startUpstreamやstartDownstreamなどが実行可能になります。\n            }\n        )\n    }\n\n    val connectionCallbacks : ConnectionCallbacks\n        get() = object : ConnectionCallbacks {\n            override fun onReconnect(connection: Connection) {\n                // Connectionが再オープンされた際にコールされます。\n            }\n\n            override fun onDisconnect(connection: Connection) {\n                // Connectionがクローズされた際にコールされます。\n            }\n\n            override fun onFailWithError(connection: Connection, exception: Exception) {\n                // Connection内部で何らかのエラーが発生した際にコールされます。\n            }\n        }\n}\n```\n\n### Start Upstream\n\nアップストリームの送信サンプルです。\n\nこのサンプルでは、基準時刻のメタデータと、文字列型のデータポイントをiSCPサーバーへ送信しています。\n\n```kotlin\nimport com.aptpod.iscp.model.BaseTime\nimport com.aptpod.iscp.model.DataId\nimport com.aptpod.iscp.model.DataPoint\nimport com.aptpod.iscp.model.UpstreamChunk\nimport com.aptpod.iscp.model.UpstreamChunkAck\nimport com.aptpod.iscp.stream.Upstream\nimport com.aptpod.iscp.stream.UpstreamCallbacks\nimport java.time.ZonedDateTime\nimport java.util.UUID\n\n/**\n * 送信するデータを永続化するかどうか\n */\nvar upstreamPersist: Boolean = false\n/**\n * オープンしたストリーム一覧\n */\nvar upstreams: MutableList\u003cUpstream\u003e = mutableListOf()\n\nfun ExampleActivity.startUpstream() {\n    // セッションIDを払い出します。\n    var sessionId = UUID.randomUUID().toString().lowercase()\n\n    // Upstreamをオープンします。\n    connection?.openUpstreamAsync(\n        sessionId = sessionId,\n        persist = upstreamPersist,\n        completion = { upstream, ex -\u003e\n            if (upstream == null) {\n                // オープン失敗。\n                return@openUpstreamAsync\n            }\n            // オープン成功\n            upstreams.add(upstream)\n\n            // 送信するデータポイントを保存したい場合や、アップストリームのエラーをハンドリングしたい場合はコールバックを設定します。\n            upstream.callbacks = this.upstreamCallbacks // UpstreamCallbacks\n\n            var date = ZonedDateTime.now() // ※マイクロ秒以下をサポートするには修正が必要です。\n            var baseTime = (date.toEpochSecond() * 1000_000_000) + date.nano // 基準時刻です。\n\n            // 基準時刻をiSCPサーバーへ送信します。\n            connection?.sendBaseTimeAsync(\n                baseTime = BaseTime(\n                    sessionId = sessionId,\n                    name = \"manual\",\n                    priority = 60,\n                    elapsedTime = 0,\n                    baseTime = baseTime),\n                persist = upstreamPersist,\n                completion = { sendBaseTimeEx -\u003e\n                    if (sendBaseTimeEx != null) {\n                        // 基準時刻の送信に失敗。\n                        return@sendBaseTimeAsync\n                    }\n                    // 基準時刻の送信に成功。\n\n                    // 文字列型のデータポイントをiSCPサーバーへ送信します。\n                    var now = ZonedDateTime.now() // ※マイクロ秒以下をサポートするには修正が必要です。\n                    upstream.writeDataPoint(\n                        dataId = DataId(\n                            name = \"greeting\",\n                            type = \"string\"),\n                        dataPoint = DataPoint(\n                            elapsedTime = ((now.toEpochSecond() * 1000_000_000) + now.nano) - baseTime, // 基準時刻からの経過時間をデータポイントの経過時間として打刻します。\n                            payload = \"hello\".toByteArray()\n                        )\n                    )\n                }\n            )\n        }\n    )\n}\n\nval ExampleActivity.upstreamCallbacks : UpstreamCallbacks\n    get() = object : UpstreamCallbacks {\n        override fun onGenerateChunk(upstream: Upstream, message: UpstreamChunk) {\n            // バッファへ書き込んだデータポイントが実際に送信される直前にコールされます。\n        }\n\n        override fun onReceiveAck(upstream: Upstream, message: UpstreamChunkAck) {\n            // データポイントの送信後に返却されるACKを受信できた場合にコールされます。\n        }\n\n        override fun onFailWithError(upstream: Upstream, error: Exception) {\n            // 内部でエラーが発生した場合にコールされます。\n        }\n\n        override fun onCloseWithError(upstream: Upstream, error: Exception) {\n            // 何らかの理由でストリームがクローズした場合にコールされます。\n            // 再度アップストリームをオープンしたい場合は、 `Connection.reopenUpstream()` を使用することにより、ストリームの設定を引き継いで別のストリームを開くことが可能です。\n        }\n\n        override fun onResume(upstream: Upstream) {\n            // 自動再接続機能が働き、再接続が行われた場合にコールされます。\n        }\n    }\n```\n\n### Start Downstream\n\nアップストリームで送信されたデータをダウンストリームで受信するサンプルです。\n\nこのサンプルでは、アップストリーム開始のメタデータ、基準時刻のメタデータ、文字列型のデータポイントを受信しています。\n\n```kotlin\nimport android.util.Log\nimport com.aptpod.iscp.model.DataFilter\nimport com.aptpod.iscp.model.DownstreamChunk\nimport com.aptpod.iscp.model.DownstreamFilter\nimport com.aptpod.iscp.model.DownstreamMetadata\nimport com.aptpod.iscp.stream.Downstream\nimport com.aptpod.iscp.stream.DownstreamCallbacks\nimport java.time.Instant\nimport java.time.ZoneId\nimport java.time.ZonedDateTime\n\n/**\n * 受信したいデータを送信している送信元ノードのUUID\n *\n * （アップストリームを行っている送信元でConnection.Configで設定したnodeIdを指定してください。）\n */\nvar targetDownstreamNodeID = \"00000000-0000-0000-0000-000000000000\"\n/**\n * オープンしたダウンストリーム一覧\n */\nvar downstreams: MutableList\u003cDownstream\u003e = mutableListOf()\n\nfun ExampleActivity.startDownstream() {\n    // ダウンストリームをオープンします。\n    connection?.openDownstreamAsync(\n        downstreamFilters = listOf(\n            DownstreamFilter(\n                sourceNodeId = targetDownstreamNodeID, // // 送信元ノードのIDを指定します。\n                dataFilters = listOf(\n                    DataFilter(\n                        name = \"#\", type = \"#\") // 受信したいデータを名称と型で指定します。この例では、ワイルドカード `#` を使用して全てのデータを取得します。\n                )\n            )\n        ),\n        completion = { downstream, ex -\u003e\n            if (downstream == null) {\n                // オープン失敗。\n                return@openDownstreamAsync\n            }\n            // オープン成功。\n            downstreams.add(downstream)\n            // 受信データを取り扱うためにデリゲートを設定します。\n            downstream.callbacks = this.downstreamCallbacks // DownstreamCallbacks\n        }\n    )\n}\n\nval ExampleActivity.downstreamCallbacks: DownstreamCallbacks\n    get() = object : DownstreamCallbacks {\n        override fun onReceiveChunk(downstream: Downstream, message: DownstreamChunk) {\n            // データポイントを読み込むことができた際にコールされます。\n            Log.d(this.javaClass.name, \"Received dataPoints sequenceNumber[${message.sequenceNumber}], sessionId[${message.upstreamInfo.sessionId}]\")\n            for (g in message.dataPointGroups) {\n                for (dp in g.dataPoints) {\n                    Log.d(this.javaClass.name, \"Received a dataPoint dataName[${g.dataId.name}], dataType[${g.dataId.type}], payload[${String(dp.payload)}]\")\n                }\n            }\n        }\n\n        override fun onReceiveMetadata(downstream: Downstream, message: DownstreamMetadata) {\n            // メタデータを受信した際にコールされます。\n            Log.d(this.javaClass.name, \"Received a metadata sourceNodeId[${message.sourceNodeId}], metadataType:${message.type}\")\n            when (message.type) {\n                DownstreamMetadata.MetadataType.BASE_TIME -\u003e {\n                    var baseTime = message.baseTime!!\n                    var date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(baseTime.baseTime / 1000_000_000, baseTime.baseTime % 1000_000_000), ZoneId.systemDefault())\n                    Log.d(this.javaClass.name, \"Received baseTime[$date], priority[${baseTime.priority}], name[${baseTime.name}]\")\n                }\n                else -\u003e {}\n            }\n        }\n\n        override fun onFailWithError(downstream: Downstream, error: Exception) {\n            // 内部でエラーが発生した場合にコールされます。\n        }\n\n        override fun onCloseWithError(downstream: Downstream, error: Exception) {\n            // 何らかの理由でストリームがクローズした場合にコールされます。\n            // 再度ダウンストリームをオープンしたい場合は、 `Connection.reopenDownstream()` を使用することにより、ストリームの設定を引き継いで別のストリームを開くことが可能です。\n        }\n\n        override fun onResume(downstream: Downstream) {\n            // 自動再接続機能が働き、再接続が行われた場合にコールされます。\n        }\n    }\n```\n\n\n### E2E Call\n\nE2E（エンドツーエンド）コールのサンプルです。\n\nコントローラノードが対象ノードに対して指示を出し、対象ノードは受信完了のリプライを行う簡単なサンプルです。\n\n```kotlin\nclass E2ECallExampleActivity  : Activity() {\n    /**\n     * 接続するintdashサーバー\n     */\n    var targetServer: String = \"https://example.com\"\n\n    /**\n     * コントローラーノードのUUID\n     */\n    var controllerNodeID: String = \"00000000-0000-0000-0000-000000000000\"\n    /**\n     * 対象ノードのUUID\n     */\n    var targetNodeID: String = \"11111111-1111-1111-1111-111111111111\"\n\n    /**\n     * コントローラーノード用のアクセストークン\n     *\n     * intdash APIで取得したアクセストークンを指定して下さい。\n     */\n    var accessTokenForController : String = \"\"\n    /**\n     * 対象ノード用のアクセストークン\n     *\n     * intdash APIで取得したアクセストークンを指定して下さい。\n     */\n    var accessTokenForTarget: String = \"\"\n\n    /**\n     * コントローラーノード用のコネクション\n     */\n    var connectionForController: Connection? = null\n    /**\n     * 対象ノード用のコネクション\n     */\n    var connectionForTarget: Connection? = null\n}\n\n//region コントローラーノードからメッセージを送信するサンプルです。このサンプルでは文字列メッセージを対象ノードに対して送信し、対象ノードからのリプライを待ちます。\n\nfun E2ECallExampleActivity.connectForController() {\n    // 接続情報のセットアップをします。\n    var urls = targetServer.split(\"://\")\n    var address: String\n    var enableTls: Boolean = false\n    if (urls.size == 1)\n    {\n        address = urls[0]\n    }\n    else\n    {\n        enableTls = urls[0] == \"https\"\n        address = urls[1]\n    }\n    // WebSocketを使って接続するように指定します。\n    var connector: IConnector = WebSocketConnector(enableTls = enableTls)\n    Connection.connectAsync(\n        address = address,\n        connector = connector,\n        tokenSource = {\n            // アクセス用のトークンを指定します。接続時に発生するイベントにより使用されます。\n            // ここでは固定のトークンを返していますが随時トークンの更新を行う実装にするとトークンの期限切れを考える必要がなくなります。\n            accessTokenForController\n        },\n        nodeId = controllerNodeID,\n        completion = { con, ex -\u003e\n            if (con == null) {\n                // 接続失敗\n                return@connectAsync\n            }\n            // 接続成功.\n            connectionForController = con\n        }\n    )\n}\n\nfun E2ECallExampleActivity.sendCall() {\n    // コールを送信し、リプライコールを受信するとコールバックが発生します。\n    connectionForController?.sendCallAndWaitReplyCallAsync(\n        upstreamCall = UpstreamCall(\n            destinationNodeId = targetNodeID,\n            name = \"greeting\",\n            type = \"string\",\n            payload = \"hello\".toByteArray()\n        ),\n        completion = { downstreamReplyCall, ex -\u003e\n            if (ex != null) {\n                // コールの送信もしくはリプライの受信に失敗。\n                return@sendCallAndWaitReplyCallAsync\n            }\n            // コールの送信及びリプライの受信に成功。\n        }\n    )\n}\n\n//endregion\n\n//region コントローラーノードからのコールを受け付け、すぐにリプライするサンプルです。\n\nfun E2ECallExampleActivity.connectForTarget() {\n    // 接続情報のセットアップをします。\n    var urls = targetServer.split(\"://\")\n    var address: String\n    var enableTls: Boolean = false\n    if (urls.size == 1)\n    {\n        address = urls[0]\n    }\n    else\n    {\n        enableTls = urls[0] == \"https\"\n        address = urls[1]\n    }\n    // WebSocketを使って接続するように指定します。\n    var connector: IConnector = WebSocketConnector(enableTls = enableTls)\n    Connection.connectAsync(\n        address = address,\n        connector = connector,\n        tokenSource = {\n            // アクセス用のトークンを指定します。接続時に発生するイベントにより使用されます。\n            // ここでは固定のトークンを返していますが随時トークンの更新を行う実装にするとトークンの期限切れを考える必要がなくなります。\n            accessTokenForTarget\n        },\n        nodeId = targetNodeID,\n        completion = { con, ex -\u003e\n            if (con == null) {\n                // 接続失敗\n                return@connectAsync\n            }\n            // 接続成功.\n            this.connectionForTarget = con\n            // DownstreamCallの受信を監視するためにコールバックを設定します。\n            con.e2ECallCallbacks = this.e2ECallCallbacks // ConnectionE2ECallCallbacks\n        }\n    )\n}\n\n//endregion\n\nval E2ECallExampleActivity.e2ECallCallbacks: ConnectionE2ECallCallbacks\n    get() = object : ConnectionE2ECallCallbacks {\n        override fun onReceiveCall(connection: Connection, downstreamCall: DownstreamCall) {\n            // DownstreamCallを受信した際にコールされます。\n            // このサンプルではDownstreamCallを受信したらすぐにリプライコールを送信します。\n            connection.sendReplyCallAsync(\n                upstreamReplyCall = UpstreamReplyCall(\n                    requestCallId = downstreamCall.callId,\n                    destinationNodeId = downstreamCall.sourceNodeId,\n                    name = \"reply_greeting\",\n                    type = \"string\",\n                    payload = \"world\".toByteArray()\n                ),\n                completion = { ex -\u003e\n                    if (ex != null) {\n                        // リプライコールの送信に失敗。\n                        return@sendReplyCallAsync\n                    }\n                    // リプライコールの送信に成功。\n                }\n            )\n        }\n\n        override fun onReceiveReplyCall(connection: Connection, downstreamReplyCall: DownstreamReplyCall) {\n            // DownstreamReplyCallを受信した際にコールされます。\n        }\n    }\n```\n\n## References\n- [APIリファレンス](https://docs.intdash.jp/api/intdash-sdk/kotlin/latest/)\n  - 過去のバージョンのリファレンスは [こちら](https://docs.intdash.jp/api/intdash-sdk/kotlin-versions)\n- [GitHub](https://github.com/aptpod/iscp-kt)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faptpod%2Fiscp-kt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faptpod%2Fiscp-kt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faptpod%2Fiscp-kt/lists"}