iCAD SXとPythonでソケット通信してみた

2023/11/29 categories:iCAD SX| tags:iCAD SX|Python|

クライアントのソケット

iCAD SXを起動するとlocalhostで通信できるようになるので、ソケットを作成して環境設定で設定したポートに接続します。

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect( ('localhost', 3999) )
stream = client.makefile('rwb', buffering=0)

コマンドの送信

こんな感じのコマンドをutf-16leでデコードして送信するとiCAD SXが受け付けてくれました。

commands: list[str]

send_data  = ''
send_data += 'license=ON\n'
send_data += 'mode=MACRO,NODISP\n'
send_data += 'ret_ent=ON\n'
send_data += '\n'.join(commands)
send_data += 'SxMsg_End'

stream.write( send_data.encode('utf-16le') )

データの受信

streamをreadして、utf-16leでデコードするとXMLの文字列が得られるようです。文字列の末尾に’\x00’が含まれているようで、xml.etree.ElementTreeのfromstringに渡すと’\x00’が使えない?のかエラーが出てしまいました。仕方がないので文字列の末尾が’\x00’のときは最後の文字を削除するようにしています。

xml = stream.read().decode('utf-16le')
if xml[-1] == '\x00':
    xml = xml[:-1]
root = ElementTree.fromstring(xml)

アクティブモデルの取得

送信するコマンドはマクロの記録で記録できるコマンドと同じようです。以下のように’;JVGSIF;GXDMY;@JVEND’でアクティブモデルなどが取得できました。あとはXMLとにらめっこするとほしいデータが得られると思います。

root = Client().get_data([';JVGSIF;GXDMY;@JVEND'])
element = root.find('sx_inf_sys')

self.active_model = None
self.active_vs = element.find('sx_vs')
self.pd = element.find('sx_pd')
self.models: list[Model] = []
for model in element.findall('sx_model'):
    self.models.append( Model(model) )
    if self.models[-1].model_id == element.attrib.get('model'):
        self.active_model = self.models[-1]

Pythonコード

以下のコードでアクティブモデルの図面名が取得できました。

import socket
from xml.etree import ElementTree


class Client:
    def get_data(self, commands: list[str]):
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect( ('localhost', 3999) )
        stream = client.makefile('rwb', buffering=0)

        send_data  = ''
        send_data += 'license=ON\n'
        send_data += 'mode=MACRO,NODISP\n'
        send_data += 'ret_ent=ON\n'
        send_data += '\n'.join(commands)
        send_data += 'SxMsg_End'

        stream.write( send_data.encode('utf-16le') )
        
        xml = stream.read().decode('utf-16le')
        if xml[-1] == '\x00':
            xml = xml[:-1]

        client.close()

        root = ElementTree.fromstring(xml)
        
        error = root.find('sx_err')
        if error is not None:
            raise Exception( '{}, {}, {} : {}'.format(error.get('ir0'), error.get('ir1'), error.get('ir2'), error.text) )
        
        return root


class Sys:
    inf = None

    def get_inf(self):
        self.inf = InfSys()


class InfSys:
    def __init__(self) -> None:

        root = Client().get_data([';JVGSIF;GXDMY;@JVEND'])
        element = root.find('sx_inf_sys')

        self.active_model = None
        self.active_vs = element.find('sx_vs')
        self.pd = element.find('sx_pd')
        self.models: list[Model] = []
        for model in element.findall('sx_model'):
            self.models.append( Model(model) )
            if self.models[-1].model_id == element.attrib.get('model'):
                self.active_model = self.models[-1]


class Model:
    def __init__(self, element: ElementTree.Element) -> None:
        self.model_id = element.attrib.get('model_id', -1)
        self.inf = InfModel(self.model_id)
    

class InfModel:
    def __init__(self, model_id: str) -> None:

        root = Client().get_data([f';JVGMIF .NAME {model_id} :', ';@JVEND'])
        inf_model = root.find('sx_inf_model')

        self.path = inf_model.attrib.get('path')
        self.name = inf_model.attrib.get('name')
        self.comment = inf_model.attrib.get('comment')
        self.passwd = inf_model.attrib.get('passwd')
        self.access = int( inf_model.attrib.get('access') )
        self.is_read_only = inf_model.attrib.get('is_read_only') != "0"
        self.is_modify = inf_model.attrib.get('is_modify') != "0"
        self.nvs = int( inf_model.attrib.get('nvs') )
        self.nwf = int( inf_model.attrib.get('nwf') )


if __name__ == '__main__':
    sys = Sys()
    sys.get_inf()
    
    print(sys.inf.active_model.inf.name)

Share post

Related Posts

コメント