0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【連載】LCIの使い方「エレベーター編」#7|エレベーターの状態取得(RequestElevatorStatus)

Last updated at Posted at 2025-12-03

こんにちは!この記事では、 株式会社Octa Roboticsが開発・提供している ロボット・設備間連携に特化したマルチベンダー型のインターフェースサービス「LCI」 について、ロボットからの利用方法を連載形式で紹介していきます。

前回の記事では、通信プロトコルの「エレベーター呼出階・行き先階指定(CallElevator)」を実装しました。

第6回はこちら

今回は、「エレベーターの状態取得(RequestElevatorStatus)」について実装を行っていきます。

エレベーターの状態取得(RequestElevatorStatus)に関しては、今回と次回の2回に分けて実装を行います。

おさらい

エレベーターの状態取得について、再確認していきましょう。

エレベーターの状態取得では、以下の手順でロボットとLCI&エレベーター間でMQTT通信を行います。

  1. ロボットからエレベーターにRequestElevatorStatusを送信
  2. エレベーターがRequestElevatorStatusを受信
  3. エレベーターからロボットにElevatorStatusを送信
  4. ロボットがElevatorStatusを受信

ElevatorStatusのresultが1の場合は成功ElevatorStatusのresultが2、3、99の場合は失敗とみなします。

エレベーターの状態取得.png

次は、トピック(メッセージの宛先)とペイロード(メッセージの中身)を確認していきましょう。

プロトコルの仕様

RequestElevatorStatusとElevatorStatusの仕様を確認していきます。

トピック(メッセージの宛先)

プロトコル トピック名
RequestElevatorStatus /lci/<bldg_id>/<bank_id>/<elevator_id>/RequestElevatorStatus/<robot_id>
ElevatorStatus /lci/<bldg_id>/<bank_id>/<elevator_id>/ElevatorStatus/<robot_id>

第3回で使用した設定ファイル:server_config_simulator_1-1.yamlを利用する場合には、以下になります。

プロトコル トピック
RequestElevatorStatus /lci/simulator/1/1/RequestElevatorStatus/_uClosc2
ElevatorStatus /lci/simulator/1/1/ElevatorStatus/_uClosc2
  • bldg_id : simulator(設定ファイル内のlci_bldg_id)
  • bank_id : 1(設定ファイル内のlci_bank_id)
  • elevator_id : 1(設定ファイル内のlci_elevator_id)
  • robot_id : _uClosc2(開発用ロボットアカウントのID)

ペイロード(メッセージの中身)

プロトコル ペイロード
RequestElevatorStatus {
"timestamp":<timestamp>,
"robot_id":<robot_id>
}
ElevatorStatus {
"result":<result>,
"floor":<floor>,
"door":<door>,
"direction":<direction>,
"timestamp":<timestamp>,
"requested_robot_id":<requested_robot_id>,
"requested_timestamp":<requested_timestamp>
}
  • timestamp : RequestElevatorStatusもしくはElevatorStatusを送信した時間(エポック秒)
  • robot_id : RequestElevatorStatusを行うロボットのID(今回は_uClosc2)
  • result : RequestElevatorStatusの結果(1:成功、2:失敗、3:その他エラー、99:管制運転中)
  • floor : 現在のエレベーターがいるフロア
  • door : ドアの開閉状態(エレベーター)
  • direction : エレベーターの移動方向(LCIでは必ず3)
  • requested_robot_id : RequestElevatorStatusを行なったロボットのID(今回は_uClosc2)
  • requested_timestamp : RequestElevatorStatusが送られてきた時間

ElevatorStatusのdoorは、ドアの状態に応じて以下の値を返します。

フロントドア状態 リアドア状態
0 not完全開 not完全開
1 完全開 not完全開
2 not完全開 完全開
3 完全開 完全開

※ 同時開放は現行法で禁止されているため、3は使われない

directionについては、LCIではサポート外のため必ず3が入ります。

RFA規格においては、以下のように定義されています。

状態
0 停止中
1 下に移動
2 上に移動

補足:ペイロード内のfloorについて

ペイロード内のfloorは、フロアの名称を表す文字列であるfloor_idに従います。

floor_idは、設定ファイル内におけるlci_floor_listに記載があります。

今回使用している設定ファイル、server_config_simulator-1-1.yamlでは以下のように記載されています。

server_config_simulator-1-1.yaml
# floor name list from the bottom to the top
lci_floor_list:
- ['B1', True, False]
- 'E'
- 'G'
- 'L'
- '1F'
- '2F'
- '3F'
- ['4F', True, False]
- 'M5'
- 'M6'
- ['8F', True, True]
- ['R', False, True]

フロアの名称であるfloor_idは、以下の規則に従って命名されています。

  • 建物図面上の略号を用いる場合

    • 例えば、'A', 'B', 'G', 'LG', 'UG', 'L', 'M', 'P', 'R'などがあります
    • 同じ略号で複数フロアがある場合は、区別をつけるため’A1’、’A2’のように数字を末尾につけます
  • 建物図面上のフロアの名称が数字の場合

    • 0,1,2,3といった数字だけでフロアが表記されている場合は、末尾に’F’をつけます
    • 例)'0F', '1F', '2F', '3F'

サンプルコードの確認

サンプルコードのエレベーター状態取得に関連する箇所を確認していきましょう。

do_request_elevator_status

ここでは、以下のことを行なっています。

  • ペイロードの作成
  • トピックの作成
  • メッセージのパブリッシュ
  • ElevatorStatusのレスポンスを待つ
lci_client.py
def do_request_elevator_status(self):
       if not self.is_registered():
           return ElevatorStatus()


       # requested_time = int(time.time() * 1000)
       requested_time = int(time.time() * 1000)/1000
       payload = json.dumps({
           'timestamp': requested_time,


           'robot_id': self.robot_id})


       api = 'RequestElevatorStatus'
       topic = f'{self.topic_prefix}/{self.elevator_id}/{api}/{self.robot_id}'


       self._publish(topic, payload)
       return self.wait_response(api, requested_time, 3.0)

メイン処理

lci_client.py
# Main routine
if __name__ == '__main__':


   #---省略---#


   #エレベーターへの状態取得
   res = lci_client.do_request_elevator_status()
   lci_client.logger.debug(res)
   time.sleep(1)

サンプルコードの改良

RequestElevatorStatusでは、エレベーターの呼び出し成功(CallElevator)から降車完了(RobotStatus, state:2)まで2秒間隔で行う必要性があります。

加えて、ElevatorStatus内のresultが2, 3, 99の場合はエラー処理を実施する必要性があります。

サンプルコードではこれらの処理は未実装です。

そのため、これらの処理を行えるようにサンプルコードを改良する必要性があります。

RequestElevatorStatusを2秒間隔で行う理由は、

  • エレベーターの到着をなるべく早く把握し、乗降をもたつかずに開始するため
    • ドアが開いた後、素早く乗降を開始することで、人の割り込み、誤乗車の可能性を下げる(通常系)
  • resultに応じたエラー情報をなるべく早く把握し、ロボットのエラー処理を速やかに実行するため
    • エレベーターのドアを通過している際にエラーが生じると連携が解除され、ドアが閉まる(異常系)

resultの意味と発生条件

はじめにElevatorStatus内のresultの意味と発生条件について確認しましょう。

評価順 の値 意味 発生条件
1 3 その他エラー ペイロードのフィールドの一部又は全部が設定されていない
2 3 その他エラー トピック名のrobot_idと、ペイロードrobot_idが一致しない
3 99 管制運転中 エレベーターが管制運転中のため連携不能
4 3 その他エラー エレベーターがロボット連携モードにも関わらず、ロボットが利用登録されていない(状態のミスマッチ)
5 2 拒否(失敗) ・エレベーターがロボット連携モードになっていない
・他のロボットが利用中
・エレベーターがロボット連携モードに遷移中
・エレベーターがロボット連携モードを解除中
6 3 その他エラー エレベーターが、設定ファイル外の階に居る
7 1 成功 エレベーターの現在の居る階、ドアの開閉状態を取得できた

resultが2(拒否)、3(その他エラー)の場合、 floor, door 及び direction はペイロードに含まれません。
99の場合は、物件に応じて含まれる場合と含まれない場合があります。(LCI Boxを導入した施設では含まれ、LCI Bridgeを導入した施設では含まれません)

各resultの値に応じたロボットの動作

先ほどの表をもとに、resultの値に対してロボットがどのように動作するかを決めていきます。

今回は仕様書をもとに以下のように定めます。

result resultの意味 ロボット側の動作
1 成功 次のステップに進む
2 拒否(失敗) 異常シーケンスに遷移
3 その他エラー 異常シーケンスに遷移
99 管制運転中 異常シーケンスに遷移

LCIからの応答が一定時間経過してもない場合は、異常シーケンスに遷移します。

フロー図

表をもとに、フロー図を作成しました。

手順としては、以下になります。

  1. stop_eventがsetされているか確認
    • stop_event = threding.Event()
    • setされている場合はループ終了
    • setされていない場合はRequestElevatorStatusを送信
  2. RequestElevatorStatusを送信
  3. ElevatorStatusを受信したか確認
    • 10秒間返信がない場合は、異常シーケンスに進む
  4. resultの値が1か確認
    • 1でない場合は、異常シーケンスに進む
  5. ペイロード内のfloorとdoorの値を変数に代入
  6. 2秒間待機  

image.png

ElevatorStatusの最大待機時間は10秒としました。

実装したコード

フロー図をもとに追記した変数や関数を記載します。

追記した変数

追記した変数とそれぞれの役割は以下です。

  • self.floor_value
    • エレベーターがいるフロアの情報を代入するための変数
  • self.door_value
    • エレベーターのドアにおける開閉情報を代入するための変数
  • self.stop_event
    • RequestElevatorStatusを終了させたい場合のフラグ
class LciClient
class LciClient:
   def __init__(self):
       self.logger = logging.getLogger('lci_client')
       self.mqtt_client = None
       self.robot_id = None
       self.timestamp = None
       self.elevator_id = None
       self.registered = False
       self.timeout_sec = None
       self.floor_list = []
       self.dry_run_registered = False

       #---追記---#
       self.floor_value = None
       self.door_value = None
       self.stop_event = threading.Event()
       #---------#

メッセージを受信し、送信したメッセージのTopicにRequestElevatorStatusが含まれている場合、エレベーターのフロア情報をself.floor_valueに、エレベーターのドアに関する開閉情報をself.door_valueに代入します

class LciClient
def msg_callback(self, topic: str, payload_kv: dict):
       parsed_topic = LciClient.ParsedTopic(topic)


       if parsed_topic.api == 'message':
           res = LciMessage()
           res.orig_api = 'message'
           res.type = payload_kv.get('type', 0)
           res.content = payload_kv.get('content', 0)
           res.robot_in_the_car = payload_kv.get('robot_in_the_car', 0)
           res.timestamp = payload_kv.get('timestamp', 0)
           self.message_queue.put(res)
           return


       elif parsed_topic.orig_api == '':
           return
       elif parsed_topic.orig_api == 'RequestElevatorStatus':
           res = ElevatorStatus()
           res.orig_api = 'RequestElevatorStatus'
             res.floor = payload_kv.get('floor', 0)
           res.door = payload_kv.get('door', 0)
           res.direction = payload_kv.get('direction', 0)
           
           #---追記---# 
           self.floor_value = res.floor
           self.door_value = res.door
           #---------#

start_request_elevator_status

後ほど説明するdo_request_elevator_status関数をスレッドとして動作させます。

class LciClient
def start_request_elevator_status(self):
    thread_request_elevator_status = threading.Thread(target = self.do_request_elevator_status)
    thread_request_elevator_status.start()
    

stop_request_elevator_status

後ほど説明するdo_request_elevator_status関数のループを終了させます。

class LciClient
def stop_request_elevator_status(self):
       self.stop_event.set()

do_request_elevator_status

フロー図をもとに実装した関数です。

2秒間隔でRequestElevatorStatusを送り続けます。

self.stop_eventをsetすることでループを終了、ElevatorStatusが10秒経過しても受信できない場合およびres.result(ElevatorStatus)の値が1でない場合self.registeredFalseにします。

self.registeredFalseになると、これ以降のシーケンスが行われなくなります。

class LciClient
def do_request_elevator_status(self):
       time_out = 10


       while not self.stop_event.is_set():
           res = self.request_elevator_status(time_out)
           if res is not None:
               if res.result != 1:
                   self.registered = False
                   self.logger.error(f"Request Elevator Status is failed code : {res.result}")
                   break

               else:
                   self.logger.debug(res)

           else:
               self.registered = False
               self.logger.error("RequestElevatorStatus is None")
               break

           time.sleep(2)

request_elevator_status

class LciClient
def request_elevator_status(self, time_out):
       if not self.is_registered():
           return ElevatorStatus()


       # requested_time = int(time.time() * 1000)
       requested_time = int(time.time() * 1000)/1000
       payload = json.dumps({
           'timestamp': requested_time,


           'robot_id': self.robot_id})


       api = 'RequestElevatorStatus'
       topic = f'{self.topic_prefix}/{self.elevator_id}/{api}/{self.robot_id}'


       self._publish(topic, payload)
       return self.wait_response(api, requested_time, time_out)

メイン処理

出発階への呼び出し後、start_request_elevator_status関数にてスレッドを動作させます。

降車完了(do_robot_status(RobotStatus.HAS_GOT_OFF))を行う前に、stop_request_elevator_status()関数によりスレッドを停止させます。

main routine
if __name__ == '__main__':
  
  #---省略---#
  
    #出発階への呼び出し
    res = lci_client.do_call_elevator('B1', '1F')
    lci_client.logger.debug(res)
    time.sleep(1)

    #エレベーターへの状態取得
    #追記
    lci_client.start_request_elevator_status()
    lci_client.logger.debug("Start Request Elevator Status")
    time.sleep(1)

    #乗り込み
    res = lci_client.do_robot_status(RobotStatus.KEEP_DOOR_OPEN)
    lci_client.logger.debug(res)
    time.sleep(1)

    #乗り込み完了
    res = lci_client.do_robot_status(RobotStatus.HAS_ENTERED)
    lci_client.logger.debug(res)
    time.sleep(1)

    #行き先階への登録
    res = lci_client.do_call_elevator('B1', '1F')
    lci_client.logger.debug(res)
    time.sleep(1)

    #降車
    res = lci_client.do_robot_status(RobotStatus.KEEP_DOOR_OPEN)
    lci_client.logger.debug(res)
    time.sleep(1)

    #エレベーター状態取得終了
    lci_client.stop_request_elevator_status()
    lci_client.logger.debug("End Request Elevator Status")
    time.sleep(1)

    #降車完了
    res = lci_client.do_robot_status(RobotStatus.HAS_GOT_OFF)
    lci_client.logger.debug(res)
    time.sleep(1)

    #利用解除
    res = lci_client.do_release()
    lci_client.logger.debug(res)
    time.sleep(1)

動作確認

今回は、下記のように一部コードを変更して、ElevatorStatusのresult値を書き換えることで動作確認を行います。

class LciClient
 def msg_callback(self, topic: str, payload_kv: dict):
       parsed_topic = LciClient.ParsedTopic(topic)


       if parsed_topic.api == 'message':
           res = LciMessage()
           res.orig_api = 'message'
           res.type = payload_kv.get('type', 0)
           res.content = payload_kv.get('content', 0)
           res.robot_in_the_car = payload_kv.get('robot_in_the_car', 0)
           res.timestamp = payload_kv.get('timestamp', 0)
           self.message_queue.put(res)
           return


       elif parsed_topic.orig_api == '':
           return
       elif parsed_topic.orig_api == 'RequestElevatorStatus':
           res = ElevatorStatus()
           res.orig_api = 'RequestElevatorStatus'
           res.floor = payload_kv.get('floor', 0)
           res.door = payload_kv.get('door', 0)
           res.direction = payload_kv.get('direction', 0)
           self.floor_value = res.floor
           self.door_value = res.door

       else:
           res = LciResponse()
           res.orig_api = parsed_topic.orig_api

        
       #ElevatorStatusのresultを変更する
       #---追記---#
       if parsed_topic.orig_api == 'RequestElevatorStatus':
           #result: 1, 2, 3, 99のいずれかを指定
           res.result = 1
       else:
           res.result = payload_kv.get('result', 0)
       #---------#

次はコードを実行した際のログを確認していきましょう。

resultが1(成功)の場合

約2秒間おきにRequestElevatorStatusを送信、RequestElevatorStatusの送信ループの停止後にRobotStatusを送信しています。

ログを確認しやすいように、エレベーター状態取得(RequestElevatorStatus)と利用解除(Release)以外のシーケンスのログは削除しています。

2025-12-03 11:27:44,642    DEBUG Sending PUBLISH (d0, q1, r0, m6), 'b'/lci/simulator/1/1/RequestElevatorStatus/_uClosc2'', ... (53 bytes)
2025-12-03 11:27:44,762    DEBUG Received PUBLISH (d0, q1, r0, m1), '/lci/simulator/1/1/ElevatorStatus/_uClosc2', ...  (156 bytes)
・・・
2025-12-03 11:27:46,813    DEBUG Sending PUBLISH (d0, q1, r0, m9), 'b'/lci/simulator/1/1/RequestElevatorStatus/_uClosc2'', ... (53 bytes)
2025-12-03 11:27:46,915    DEBUG Received PUBLISH (d0, q1, r0, m1), '/lci/simulator/1/1/ElevatorStatus/_uClosc2', ...  (156 bytes)
・・・
2025-12-03 11:27:48,925    DEBUG Sending PUBLISH (d0, q1, r0, m11), 'b'/lci/simulator/1/1/RequestElevatorStatus/_uClosc2'', ... (53 bytes)
2025-12-03 11:27:49,071    DEBUG Received PUBLISH (d0, q1, r0, m1), '/lci/simulator/1/1/ElevatorStatus/_uClosc2', ...  (157 bytes)
2025-12-03 11:27:50,098    DEBUG End Request Elevator Status
2025-12-03 11:27:51,101    DEBUG Sending PUBLISH (d0, q1, r0, m13), 'b'/lci/simulator/1/1/RobotStatus/_uClosc2'', ... (69 bytes)
2025-12-03 11:27:51,136    DEBUG Received PUBACK (Mid: 13)
2025-12-03 11:27:51,255    DEBUG Received PUBLISH (d0, q1, r0, m1), '/lci/simulator/1/1/RobotStatusResult/_uClosc2', ...  (119 bytes)

resultが2(拒否)の場合

RequestElevatorStatus以降のシーケンスを行わず、利用解除後に連携を終了しています。

2025-09-26 09:40:25,109    DEBUG Sending PUBLISH (d0, q1, r0, m6), 'b'/lci/simulator/1/1/RequestElevatorStatus/_uClosc2'', ... (53 bytes)
2025-09-26 09:40:25,109    DEBUG Start Request Elevator Status
2025-09-26 09:40:25,150    DEBUG Received PUBACK (Mid: 6)
2025-09-26 09:40:25,265    DEBUG Received PUBLISH (d0, q1, r0, m1), '/lci/simulator/1/1/ElevatorStatus/_uClosc2', ...  (157 bytes)
2025-09-26 09:40:25,266    DEBUG Sending PUBACK (Mid: 1)
2025-09-26 09:40:25,273    ERROR Request Elevator Status is failed code : 2
2025-09-26 09:40:26,114    DEBUG None
2025-09-26 09:40:27,120    DEBUG None
ERROR:root:CallElevator is failed!
2025-09-26 09:40:28,121    DEBUG None
2025-09-26 09:40:29,127    DEBUG None
2025-09-26 09:40:30,129    DEBUG End Request Elevator Status
2025-09-26 09:40:31,135    DEBUG Sending PUBLISH (d0, q1, r0, m7), 'b'/lci/simulator/1/1/Release/_uClosc2'', ... (53 bytes)
2025-09-26 09:40:31,171    DEBUG Received PUBACK (Mid: 7)
2025-09-26 09:40:31,277    DEBUG Received PUBLISH (d0, q1, r0, m1), '/lci/simulator/1/1/ReleaseResult/_uClosc2', ...  (115 bytes)
2025-09-26 09:40:31,285    DEBUG Sending PUBACK (Mid: 1)
2025-09-26 09:40:31,298    DEBUG orig_api: 'Release', result: '1', elevator_id: '1', timestamp: '1758847231.214', requested_robot_id: '_uClosc2', requested_timestamp: '1758847231.134'

resultが3(その他エラー)の場合

RequestElevatorStatus以降のシーケンスを行わず、利用解除後に連携を終了しています。

2025-09-26 09:47:13,328    DEBUG Sending PUBLISH (d0, q1, r0, m6), 'b'/lci/simulator/1/1/RequestElevatorStatus/_uClosc2'', ... (53 bytes)
2025-09-26 09:47:13,328    DEBUG Start Request Elevator Status
2025-09-26 09:47:13,364    DEBUG Received PUBACK (Mid: 6)
2025-09-26 09:47:13,435    DEBUG Received PUBLISH (d0, q1, r0, m1), '/lci/simulator/1/1/ElevatorStatus/_uClosc2', ...  (157 bytes)
2025-09-26 09:47:13,436    DEBUG Sending PUBACK (Mid: 1)
2025-09-26 09:47:13,436    ERROR Request Elevator Status is failed code : 3
2025-09-26 09:47:14,329    DEBUG None
2025-09-26 09:47:15,331    DEBUG None
ERROR:root:CallElevator is failed!
2025-09-26 09:47:16,335    DEBUG None
2025-09-26 09:47:17,340    DEBUG None
2025-09-26 09:47:18,344    DEBUG End Request Elevator Status
2025-09-26 09:47:19,346    DEBUG Sending PUBLISH (d0, q1, r0, m7), 'b'/lci/simulator/1/1/Release/_uClosc2'', ... (53 bytes)
2025-09-26 09:47:19,380    DEBUG Received PUBACK (Mid: 7)
2025-09-26 09:47:19,491    DEBUG Received PUBLISH (d0, q1, r0, m1), '/lci/simulator/1/1/ReleaseResult/_uClosc2', ...  (115 bytes)
2025-09-26 09:47:19,491    DEBUG Sending PUBACK (Mid: 1)
2025-09-26 09:47:19,512    DEBUG orig_api: 'Release', result: '1', elevator_id: '1', timestamp: '1758847639.411', requested_robot_id: '_uClosc2', requested_timestamp: '1758847639.346'

resutが99(その他エラー)の場合

RequestElevatorStatus以降のシーケンスを行わず、利用解除後に連携を終了しています。

2025-09-26 09:51:25,751    DEBUG Sending PUBLISH (d0, q1, r0, m6), 'b'/lci/simulator/1/1/RequestElevatorStatus/_uClosc2'', ... (53 bytes)
2025-09-26 09:51:25,752    DEBUG Start Request Elevator Status
2025-09-26 09:51:25,791    DEBUG Received PUBACK (Mid: 6)
2025-09-26 09:51:25,877    DEBUG Received PUBLISH (d0, q1, r0, m1), '/lci/simulator/1/1/ElevatorStatus/_uClosc2', ...  (157 bytes)
2025-09-26 09:51:25,877    DEBUG Sending PUBACK (Mid: 1)
2025-09-26 09:51:25,916    ERROR Request Elevator Status is failed code : 99
2025-09-26 09:51:26,757    DEBUG None
2025-09-26 09:51:27,762    DEBUG None
ERROR:root:CallElevator is failed!
2025-09-26 09:51:28,768    DEBUG None
2025-09-26 09:51:29,773    DEBUG None
2025-09-26 09:51:30,776    DEBUG End Request Elevator Status
2025-09-26 09:51:31,778    DEBUG Sending PUBLISH (d0, q1, r0, m7), 'b'/lci/simulator/1/1/Release/_uClosc2'', ... (53 bytes)
2025-09-26 09:51:31,815    DEBUG Received PUBACK (Mid: 7)
2025-09-26 09:51:31,987    DEBUG Received PUBLISH (d0, q1, r0, m1), '/lci/simulator/1/1/ReleaseResult/_uClosc2', ...  (115 bytes)
2025-09-26 09:51:31,987    DEBUG Sending PUBACK (Mid: 1)
2025-09-26 09:51:32,005    DEBUG orig_api: 'Release', result: '1', elevator_id: '1', timestamp: '1758847891.943', requested_robot_id: '_uClosc2', requested_timestamp: '1758847891.777'

その他

連載記事リスト

他の記事もチェックしてみてください!


次回の記事では、引き続き通信プロトコルの「エレベーターの状態取得」についてサンプルコードを改良する予定です!
ご興味があれば「いいね」やフォローをいただけると励みになります!


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?