콘텐츠로 이동
무료 평가 시작

라이선스 설치와 확인

PDComm SDK 라이브러리는 새 통신을 시작할 때 라이선스를 확인합니다. 유효한 라이선스가 없거나 평가 기간이 끝났으면 새 통신을 시작하지 않고 전용 오류를 돌려줍니다. 이미 연결된 통신은 그대로 계속됩니다.

라이선스는 텍스트 파일 하나로 받습니다. 이 파일을 응용 프로그램이 찾을 수 있는 곳에 두거나 API로 설치하면 됩니다.

라이선스는 SDK에만 필요합니다. 라이선스는 모듈 단위로 기능을 허용합니다.

구성 요소 모듈 라이선스
SECS/GEM SDK secs-gem. GEM300 기능 목록(capability catalog)은 gem300 필요. 호스트 ID에 묶입니다.
Modbus SDK modbus. Modbus/TCP Security를 쓰면 modbus-security도 필요합니다. 필요
OPC UA SDK opcua-client, opcua-server(PubSub 포함) 필요
MQTT mqtt 라이선스 모듈로 정의되어 있습니다. MQTT는 OPC UA 시뮬레이터 프로그램으로 쓰며, 시뮬레이터에는 라이선스가 필요 없습니다.
시뮬레이터(GUI, 헤드리스, MCP), 가상 시리얼 포트 도구, 문서 CLI 해당 없음 필요 없습니다. 무료입니다.

SECS/GEM 시뮬레이터(GUI, 헤드리스, MCP)와 Modbus 시뮬레이터 GUI는 SDK 라이브러리를 쓰므로, 그 프로그램에서만 동작하는 도구용 라이선스를 내장하고 시작할 때 설치합니다. 별도로 설치할 것은 없습니다. GUI에서는 Help 메뉴의 About 창에서 라이선스 상태와 호스트 ID를 봅니다.

종류(kind) 용도 유효 기간 호스트 ID
evaluation 30일 평가판 발급 시각부터 30일 묶이지 않습니다.
developer 응용 프로그램 개발과 배포 기간 없음. 유지보수 종료일(updatesUntil)이 있습니다. SECS/GEM 모듈만 개발 PC의 호스트 ID에 묶입니다.
runtime SECS/GEM 장비별 실행 계약에 따릅니다. 장비 PC의 호스트 ID에 묶입니다.
tool PDComm 시뮬레이터에 내장 기간 없음 묶이지 않습니다. 지정된 프로그램에서만 동작합니다.

평가판 라이선스는 모든 모듈을 30일 동안 허용합니다. 기능 제한이나 실행 시간, 실행 횟수 제한은 없습니다. 기간이 끝나면 상태가 expired가 되고 새 통신을 시작하지 않습니다. 남은 날짜는 상태 JSON의 days_left로 확인합니다.

개발 라이선스와 배포(OPC UA, Modbus, MQTT)

섹션 제목: “개발 라이선스와 배포(OPC UA, Modbus, MQTT)”

OPC UA, Modbus, MQTT는 개발 라이선스로 만든 응용 프로그램을 로열티 없이 배포합니다. 배포하는 응용 프로그램에 라이선스 텍스트를 넣어 함께 보냅니다. 방법은 둘 중 하나입니다.

  • 응용 프로그램이 시작할 때 라이선스 텍스트를 API로 설치합니다. 텍스트는 실행 파일에 넣거나 리소스로 함께 배포합니다.
  • 라이선스 파일을 pdcomm.license라는 이름으로 실행 파일 옆에 둡니다.

SECS/GEM(secs-gem, gem300) 모듈은 호스트 ID에 묶입니다. 개발 라이선스에는 개발에 쓰는 PC의 호스트 ID를, 런타임 라이선스에는 장비 PC의 호스트 ID를 적어 발급합니다. 평가판은 호스트 ID에 묶이지 않습니다.

장비마다 다음 순서로 진행합니다.

  1. 장비 PC에서 pdcomm-license host-id를 실행하거나 호스트 ID API로 호스트 ID를 읽습니다.
  2. 호스트 ID를 보내 런타임 라이선스를 받습니다.
  3. 받은 파일을 장비 PC의 응용 프로그램 옆에 pdcomm.license로 두거나 다른 설치 방법으로 설치합니다.

개발 라이선스의 updatesUntil은 유지보수 종료일입니다. SDK의 빌드 날짜(UTC)가 이 날짜보다 늦으면 상태가 updates-expired가 됩니다. 종료일 전에 빌드된 SDK는 계속 쓸 수 있습니다. 새 SDK로 올리려면 유지보수를 갱신합니다. SDK의 빌드 날짜는 pdcomm-license version의 출력이나 상태 JSON의 build_date에 있습니다.

호스트 ID는 PDH1-XXXX-XXXX-XXXX-XXXX 형식입니다. X는 대문자 16진수입니다. 운영체제가 기록한 컴퓨터 고유 ID를 SHA-256으로 해시한 값의 앞 8바이트이므로, 호스트 ID에서 원래 ID를 알아낼 수 없습니다.

운영체제 원본 ID 직접 확인하는 방법
Linux /etc/machine-id. 없으면 /var/lib/dbus/machine-id cat /etc/machine-id
Windows 레지스트리 HKLM\SOFTWARE\Microsoft\Cryptography의 MachineGuid reg query HKLM\SOFTWARE\Microsoft\Cryptography /v MachineGuid
macOS 하드웨어 UUID(gethostuuid()) ioreg -rd1 -c IOPlatformExpertDevice의 IOPlatformUUID

호스트 ID는 다음 방법으로 읽습니다. 모두 같은 값을 돌려줍니다.

방법 사용법
명령 pdcomm-license host-id
C(SECS/GEM) pdcomm_license_host_id
C(OPC UA) pdcomm_opcua_license_host_id
C++ pdcomm::License::hostId(), pdcomm::modbus::License::hostId(), pdcomm::opcua::license::host_id()
.NET PDComm.License.HostId(), PDComm.Modbus.License.HostId(), PDComm.OpcUa.License.HostId()
Modbus JSON {"action":"host_id"} → {"host_id":"PDH1-…"}

호스트 ID가 바뀌는 경우는 다음과 같습니다. 바뀌면 새 호스트 ID로 라이선스를 다시 발급받습니다.

  • 운영체제를 다시 설치했습니다. Linux의 machine-id와 Windows의 MachineGuid가 새로 만들어집니다.
  • macOS 컴퓨터의 메인보드를 교체했습니다.
  • 가상 머신을 복제했습니다. 복제본끼리 같은 ID를 가질 수도 있습니다.

컨테이너에는 /etc/machine-id가 없는 경우가 많습니다. 이때는 호스트 ID를 읽을 수 없어 pdcomm-license host-id가 host id unavailable 오류로 끝나고, 호스트 ID에 묶인 라이선스는 host-mismatch가 됩니다. 컨테이너에 고정된 /etc/machine-id를 만들거나 호스트의 파일을 읽기 전용으로 연결합니다.

라이선스 파일은 다음 블록이 하나 이상 들어 있는 텍스트 파일입니다.

-----BEGIN PDCOMM LICENSE-----
eyJ2IjoxLCJpZCI6ImxpY18…(생략)….Fq9cz…(생략)
-----END PDCOMM LICENSE-----
  • 블록 안의 내용은 서명되어 있습니다. 한 글자라도 바꾸면 그 블록은 거부됩니다.
  • 블록 밖의 텍스트는 무시하므로 설명을 적어 두어도 됩니다. 줄 끝은 LF와 CRLF 모두 됩니다.
  • 라이선스 여러 개를 한 파일에 이어 붙여도 됩니다. 예를 들어 개발 라이선스와 런타임 라이선스를 한 pdcomm.license에 넣을 수 있습니다.
  • 파일 하나는 1 MiB를 넘을 수 없습니다.

라이브러리는 다음 위치를 모두 읽고 찾은 라이선스를 합칩니다. 한 위치에서 찾은 라이선스가 다른 위치의 라이선스를 가리지 않습니다. 같은 모듈을 허용하는 라이선스가 여럿이면 평가판이 아닌 라이선스를 먼저 씁니다.

순서 위치 설명
1 API로 설치한 텍스트 API로 설치하기를 봅니다.
2 환경 변수 PDCOMM_LICENSE 라이선스 텍스트 자체
3 환경 변수 PDCOMM_LICENSE_FILE 라이선스 파일의 경로
4 실행 파일 옆의 pdcomm.license. 없으면 SDK 라이브러리 옆의 pdcomm.license 응용 프로그램과 함께 배포할 때 씁니다.
5 사용자 설정 폴더의 pdcomm.license 아래 표를 봅니다.
6 시스템 설정 폴더의 pdcomm.license 아래 표를 봅니다.
운영체제 사용자 설정 폴더 시스템 설정 폴더
Linux $XDG_CONFIG_HOME/pdcomm/. 변수가 없으면 ~/.config/pdcomm/ /etc/pdcomm/
macOS ~/Library/Application Support/PDComm/. 없으면 $XDG_CONFIG_HOME/pdcomm/ 또는 ~/.config/pdcomm/ /etc/pdcomm/
Windows %APPDATA%\PDComm\ %ProgramData%\PDComm\
  • 위치마다 먼저 찾은 파일 하나만 씁니다. 예를 들어 실행 파일 옆에 파일이 있으면 라이브러리 옆은 보지 않습니다.
  • 값이 빈 환경 변수는 없는 것으로 봅니다.
  • 라이브러리는 확인할 때마다 파일을 다시 읽습니다. 파일을 바꾸면 프로그램을 다시 시작하지 않아도 다음 새 통신부터 적용됩니다.
  • PDCOMM_LICENSE_FILE이 가리키는 파일을 읽을 수 없으면 무시하지 않고 상태 JSON의 sources에 오류로 남깁니다.
터미널 창
# 이 셸에서 시작하는 프로그램에만 적용합니다.
export PDCOMM_LICENSE_FILE="$HOME/pdcomm/pdcomm.license"
# 이 사용자의 모든 프로그램에 적용합니다(Linux).
mkdir -p ~/.config/pdcomm
cp pdcomm.license ~/.config/pdcomm/pdcomm.license

API로 설치한 텍스트는 그 API를 부른 라이브러리에만 적용됩니다. SECS/GEM, Modbus, OPC UA 라이브러리를 한 프로세스에서 함께 쓰면 라이브러리마다 설치합니다. 환경 변수와 파일은 모든 라이브러리가 읽습니다.

언어 SECS/GEM Modbus OPC UA
C pdcomm_license_install JSON 동작 license_install pdcomm_opcua_license_install
C++ pdcomm::License::install (pdcomm_license.hpp) pdcomm::modbus::License::install (pdcomm/modbus/pdcomm_modbus_license.hpp) pdcomm::opcua::license::install (pdcomm/opcua/pdcomm_opcua.hpp)
.NET PDComm.License.Install PDComm.Modbus.License.Install PDComm.OpcUa.License.Install
  • 다시 설치하면 이전에 API로 설치한 텍스트를 바꿉니다. 빈 텍스트를 설치하면 API로 설치한 텍스트를 지웁니다.
  • 서명이 맞는 라이선스 블록이 하나도 없는 텍스트는 거부하고, 이전에 설치한 텍스트를 그대로 둡니다. C(SECS/GEM)는 PDCOMM_INVALID_DATA를, Modbus는 오류 코드 license_invalid를 돌려줍니다. 1 MiB를 넘는 텍스트는 잘못된 인수입니다.
  • 만료된 라이선스도 서명이 맞으면 설치됩니다. 설치 결과가 아니라 상태로 유효한지 확인합니다.
/* SECS/GEM: 통신을 시작하기 전에 한 번 설치합니다. */
static const char license_text[] = "-----BEGIN PDCOMM LICENSE-----\n…\n-----END PDCOMM LICENSE-----\n";
PdcommStatus status = pdcomm_license_install((const uint8_t *)license_text, sizeof license_text - 1);
if (status != PDCOMM_OK) {
/* PDCOMM_INVALID_DATA: 서명이 맞는 라이선스가 없습니다. */
}
// .NET(SECS/GEM): 응용 프로그램과 함께 배포한 라이선스 파일을 설치합니다.
PDComm.License.Install(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "app.license")));
{"action":"license_install","text":"-----BEGIN PDCOMM LICENSE-----\n…\n-----END PDCOMM LICENSE-----\n"}
{"ok":true,"result":{"installed":1}}

installed는 서명이 맞는 블록의 수입니다.

확인하는 호출과 거부될 때의 동작

섹션 제목: “확인하는 호출과 거부될 때의 동작”

라이선스는 새 통신을 시작하는 호출에서만 확인합니다. 인수 검사가 먼저이므로 잘못된 인수는 라이선스와 상관없이 인수 오류를 돌려줍니다.

제품 확인하는 호출 모듈 거부될 때
SECS/GEM pdcomm_tcp_create, pdcomm_tcp_start, pdcomm_secs1_create, pdcomm_secs1_start secs-gem C는 PDCOMM_LICENSE_REQUIRED(16), C++는 상태 16인 pdcomm::Error(what()이 PDComm license required로 시작), .NET은 Status가 LicenseRequired인 PdcommException
SECS/GEM pdcomm_gem300_capability_catalog_create, pdcomm_gem300_capability_catalog_replace gem300 위와 같습니다.
Modbus JSON 동작 connect modbus. 구성에 security가 있으면 modbus-security도 PDCOMM_MODBUS_SERVICE_ERROR(5)와 오류 코드 license_required
OPC UA pdcomm_opcua_discover, pdcomm_opcua_client_connect opcua-client 상태 11과 오류 문장
OPC UA pdcomm_opcua_server_create opcua-server 위와 같습니다.

이미 만든 끝점, 맺어진 연결, 실행 중인 서버는 라이선스가 만료되거나 바뀌어도 계속 동작합니다. 끝점을 멈춘 뒤 다시 시작하거나 다시 연결하면 그때 다시 확인합니다.

거부 메시지는 다음 형식입니다. 괄호 안이 상태입니다.

PDComm license required for module 'secs-gem' (missing): no valid license for this module was found. Existing connections keep running. Install a license with the license API, PDCOMM_LICENSE_FILE or a pdcomm.license file.

Modbus는 오류 응답에 코드와 라이선스 상태를 함께 담습니다.

{"code":"license_required","error":"PDComm license required for module 'modbus' (expired): license lic_… has expired. …","license":{"license_id":"lic_…","module":"modbus","state":"expired"},"ok":false}

SECS/GEM C ABI에는 마지막 오류 문장이 없습니다. 상태 16을 받으면 상태 확인 API로 원인을 봅니다.

언어 SECS/GEM Modbus OPC UA
C pdcomm_license_status JSON 동작 license_status pdcomm_opcua_license_status
C++ pdcomm::License::status() pdcomm::modbus::License::status() pdcomm::opcua::license::status()
.NET PDComm.License.Status() PDComm.Modbus.License.Status() PDComm.OpcUa.License.Status()

C 함수는 JSON 텍스트를 호출자 버퍼에 복사합니다. 버퍼가 작으면 버퍼를 바꾸지 않고 필요한 크기를 알려 줍니다. .NET은 JsonElement를 돌려줍니다. 상태 JSON의 주요 필드는 다음과 같습니다.

필드 내용
build_date 이 SDK의 빌드 날짜(UTC)
host_id, host_id_error 이 컴퓨터의 호스트 ID. 읽지 못하면 null이고 이유가 host_id_error에 있습니다.
program 실행 파일 이름(소문자, 확장자 제외)
keys 이 SDK가 검증하는 서명 키
sources 찾은 위치(source, location), 블록 수(blocks), 읽기 오류(error)
licenses 블록마다 라이선스 내용과 검증 결과(verification: valid, malformed, unknown-key, invalid-signature)
modules 모듈마다 state, granted, days_left, license_id, kind, expires_at, rejected, host_bound

sources의 source는 api, env:PDCOMM_LICENSE, env:PDCOMM_LICENSE_FILE, executable-dir, library-dir, user-config, system-config 중 하나입니다.

SDK 패키지에는 라이선스를 확인하는 pdcomm-license 명령이 들어 있습니다.

명령 하는 일
pdcomm-license host-id 이 컴퓨터의 호스트 ID를 출력합니다.
pdcomm-license inspect <파일> [--json] 파일의 블록마다 서명 검증 결과와 내용을 보여 줍니다.
pdcomm-license verify <파일> [--module <모듈>]... [--program <이름>] [--json] 이 컴퓨터, 지금 시각, 이 SDK 빌드 기준으로 모듈마다 상태를 보여 줍니다.
pdcomm-license status [--json] 환경 변수와 설정 폴더에서 찾은 라이선스로 상태를 보여 줍니다.
pdcomm-license version 도구 버전, 빌드 날짜, 검증하는 키를 출력합니다.

종료 코드는 0이 성공, 1이 허용되지 않음 또는 유효한 라이선스 없음, 2가 사용법이나 파일 오류입니다. verify는 --module을 주면 지정한 모듈이 모두 허용될 때, 주지 않으면 한 모듈이라도 허용될 때 0으로 끝납니다.

$ pdcomm-license verify pdcomm.license --module secs-gem --module modbus
secs-gem: evaluation (25 days left) [lic_…]
modbus: evaluation (25 days left) [lic_…]

status는 pdcomm-license 자신의 관점입니다. 환경 변수와 설정 폴더는 응용 프로그램과 같게 보이지만, 응용 프로그램이 API로 설치한 텍스트와 응용 프로그램 실행 파일 옆의 파일은 보이지 않습니다. 응용 프로그램이 보는 상태는 상태 API로 확인합니다.

상태 뜻 해결
missing 그 모듈을 허용하는 유효한 라이선스가 없습니다. 상태 JSON의 sources에 라이선스 파일이 나타나는지, 라이선스의 modules에 그 모듈이 있는지 확인합니다. rejected가 있으면 아래 거부 이유 표를 봅니다.
expired 유효 기간(expiresAt)이 지났습니다. 평가판은 30일 뒤에 이렇게 됩니다. 개발 라이선스나 새 라이선스를 받습니다.
not-yet-valid 유효 기간이 아직 시작되지 않았습니다(notBefore). 컴퓨터의 시각과 시간대를 확인합니다.
updates-expired SDK가 유지보수 종료일(updatesUntil) 뒤에 빌드되었습니다. 종료일 전에 빌드된 SDK를 쓰거나 유지보수를 갱신합니다.
program-mismatch 도구용 라이선스가 이 프로그램을 허용하지 않습니다. 도구용 라이선스는 PDComm 시뮬레이터 전용입니다. 응용 프로그램에는 평가판이나 개발 라이선스를 씁니다.
host-mismatch 호스트 ID에 묶인 라이선스(SECS/GEM 개발·런타임)에 이 컴퓨터가 없습니다. pdcomm-license host-id의 값을 보내 라이선스를 다시 받습니다. 호스트 ID를 읽지 못하는 컴퓨터는 호스트 ID를 봅니다.

라이선스 블록 자체가 거부되면 그 블록은 아무것도 허용하지 않습니다. 상태는 missing이고, 거부 이유는 modules의 rejected와 licenses의 verification에 나타납니다.

거부 이유 뜻 해결
malformed 블록을 해석할 수 없습니다. 복사하다 글자가 빠졌거나 지원하지 않는 형식입니다. 받은 파일을 그대로 다시 복사합니다. 시작 줄과 끝 줄을 포함해 블록 전체를 복사합니다.
unknown-key 이 SDK가 모르는 키로 서명했습니다. 시험용 라이선스를 정식 SDK에 썼거나, 더 새로운 키로 발급된 라이선스입니다. 라이선스를 다시 받거나 SDK를 올립니다.
invalid-signature 서명이 맞지 않습니다. 블록 내용이 바뀌었습니다. 블록을 고치지 않았는지 확인하고, 받은 원본을 다시 씁니다.

어느 위치의 라이선스가 쓰였는지 알고 싶으면 PDCOMM_LICENSE_NO_SEARCH=1을 지정합니다. 그러면 API로 설치한 텍스트와 PDCOMM_LICENSE, PDCOMM_LICENSE_FILE만 읽고 파일 위치(4~6번)는 찾지 않습니다.

터미널 창
PDCOMM_LICENSE_NO_SEARCH=1 PDCOMM_LICENSE_FILE=./pdcomm.license pdcomm-license status