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)