mtchart-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ Proprietary Commercial License
2
+
3
+ Copyright (c) 2026 MTD Softwares.
4
+
5
+ This SDK is provided as a commercial/proprietary component derived from
6
+ MTChart Pro. Use, redistribution, sublicensing, resale, or incorporation into
7
+ third-party products requires written authorization from MTD Softwares.
8
+
9
+ Developers may use this local copy only for evaluation, internal testing, and
10
+ integration planning unless a separate commercial license grants broader
11
+ rights.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
@@ -0,0 +1,6 @@
1
+ include README.md
2
+ include LICENSE.md
3
+ recursive-include examples *.py
4
+ global-exclude __pycache__/*
5
+ global-exclude *.py[cod]
6
+ global-exclude *.db
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: mtchart-sdk
3
+ Version: 0.1.0
4
+ Summary: SDK para criar sistemas de monitoramento termico, rastreabilidade e relatorios derivados do MTChart Pro.
5
+ Author: MTD Softwares
6
+ License-Expression: LicenseRef-Proprietary
7
+ Project-URL: Homepage, https://github.com/Romero-Softwares/mtchart_sdk
8
+ Project-URL: Repository, https://github.com/Romero-Softwares/mtchart_sdk
9
+ Project-URL: Documentation, https://github.com/Romero-Softwares/mtchart_sdk#readme
10
+ Project-URL: Issues, https://github.com/Romero-Softwares/mtchart_sdk/issues
11
+ Keywords: monitoramento,processo-termico,rastreabilidade,aviacao,industrial
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE.md
22
+ Provides-Extra: reports
23
+ Requires-Dist: openpyxl>=3.1; extra == "reports"
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # MTChart SDK
29
+
30
+ SDK inicial para desenvolvedores criarem sistemas semelhantes ao MTChart Pro usando classes e metodos reutilizaveis.
31
+
32
+ Esta primeira versao fica separada do aplicativo desktop e entrega um nucleo limpo para:
33
+
34
+ - cadastrar processos termicos;
35
+ - validar leituras de temperatura;
36
+ - calcular previsao de saida;
37
+ - registrar catalogo local de pecas e PN;
38
+ - montar dados de rastreabilidade sem depender da interface Flet.
39
+
40
+ ## Instalacao
41
+
42
+ ```powershell
43
+ python -m pip install mtchart-sdk
44
+ ```
45
+
46
+ ## Exemplo rapido
47
+
48
+ ```python
49
+ from datetime import datetime
50
+
51
+ from mtchart_sdk import MTChartService, PartItem, PenConfig, ProcessInput
52
+
53
+ service = MTChartService()
54
+
55
+ process = service.create_process(
56
+ ProcessInput(
57
+ report_number="CH-0001",
58
+ project="OS-123",
59
+ process_name="Alivio de fragilizacao",
60
+ oven="Forno 01",
61
+ relief_hours=3,
62
+ pen=PenConfig(id="1", name="Pena 1", stabilization_target=189, low_limit=175.9),
63
+ items=[
64
+ PartItem(name="Suporte", pn="PN-001", sn="SN-001", qty=1),
65
+ ],
66
+ started_at=datetime(2026, 6, 28, 8, 0, 0),
67
+ )
68
+ )
69
+
70
+ reading = service.evaluate_reading(process, value=188.7)
71
+
72
+ print(process.report_number)
73
+ print(reading.status)
74
+ print(reading.can_start_process)
75
+ ```
76
+
77
+ ## Estrutura
78
+
79
+ - `mtchart_sdk.models`: modelos de dados do processo.
80
+ - `mtchart_sdk.rules`: regras puras de temperatura e tempo.
81
+ - `mtchart_sdk.storage`: catalogo SQLite local de pecas.
82
+ - `mtchart_sdk.service`: fachada principal para uso por outros sistemas.
83
+ - `mtchart_sdk.cli`: demonstracao de linha de comando para validar instalacao.
84
+ - `examples/basic_process.py`: exemplo minimo executavel.
85
+
86
+ ## API principal
87
+
88
+ - `MTChartService.create_process(data)`: normaliza pecas, calcula quantidade total e previsao de saida.
89
+ - `MTChartService.evaluate_reading(process, value)`: classifica leitura como `OK`, `LOW`, `HIGH` ou `N/A`.
90
+ - `MTChartService.search_parts(term)`: consulta o catalogo local de pecas por nome ou PN.
91
+ - `clean_identifier(value)`: remove prefixos comuns como `PN:`, `P/N:`, `LOTE:` e `BATCH:`.
92
+
93
+ ## CLI de demonstracao
94
+
95
+ Depois de instalar localmente, rode:
96
+
97
+ ```powershell
98
+ mtchart-sdk-demo --value 188.7
99
+ ```
100
+
101
+ Ou sem instalar como script:
102
+
103
+ ```powershell
104
+ python -m mtchart_sdk.cli --catalog-db .tmp_demo.db --value 188.7
105
+ ```
106
+
107
+ O comando cria um processo ficticio, avalia a leitura e retorna um JSON com
108
+ status, previsao de saida e resultados do catalogo local.
109
+
110
+ ## Validacao local
111
+
112
+ Para validar uma copia local do SDK durante desenvolvimento:
113
+
114
+ ```powershell
115
+ python tools/validate_package.py
116
+ ```
117
+
118
+ O validador confere metadados, compila os modulos, roda testes e executa o
119
+ exemplo/CLI de demonstracao.
120
+
121
+ ## Status
122
+
123
+ Versao inicial. A API esta pronta para instalacao, testes locais e evolucao incremental.
@@ -0,0 +1,96 @@
1
+ # MTChart SDK
2
+
3
+ SDK inicial para desenvolvedores criarem sistemas semelhantes ao MTChart Pro usando classes e metodos reutilizaveis.
4
+
5
+ Esta primeira versao fica separada do aplicativo desktop e entrega um nucleo limpo para:
6
+
7
+ - cadastrar processos termicos;
8
+ - validar leituras de temperatura;
9
+ - calcular previsao de saida;
10
+ - registrar catalogo local de pecas e PN;
11
+ - montar dados de rastreabilidade sem depender da interface Flet.
12
+
13
+ ## Instalacao
14
+
15
+ ```powershell
16
+ python -m pip install mtchart-sdk
17
+ ```
18
+
19
+ ## Exemplo rapido
20
+
21
+ ```python
22
+ from datetime import datetime
23
+
24
+ from mtchart_sdk import MTChartService, PartItem, PenConfig, ProcessInput
25
+
26
+ service = MTChartService()
27
+
28
+ process = service.create_process(
29
+ ProcessInput(
30
+ report_number="CH-0001",
31
+ project="OS-123",
32
+ process_name="Alivio de fragilizacao",
33
+ oven="Forno 01",
34
+ relief_hours=3,
35
+ pen=PenConfig(id="1", name="Pena 1", stabilization_target=189, low_limit=175.9),
36
+ items=[
37
+ PartItem(name="Suporte", pn="PN-001", sn="SN-001", qty=1),
38
+ ],
39
+ started_at=datetime(2026, 6, 28, 8, 0, 0),
40
+ )
41
+ )
42
+
43
+ reading = service.evaluate_reading(process, value=188.7)
44
+
45
+ print(process.report_number)
46
+ print(reading.status)
47
+ print(reading.can_start_process)
48
+ ```
49
+
50
+ ## Estrutura
51
+
52
+ - `mtchart_sdk.models`: modelos de dados do processo.
53
+ - `mtchart_sdk.rules`: regras puras de temperatura e tempo.
54
+ - `mtchart_sdk.storage`: catalogo SQLite local de pecas.
55
+ - `mtchart_sdk.service`: fachada principal para uso por outros sistemas.
56
+ - `mtchart_sdk.cli`: demonstracao de linha de comando para validar instalacao.
57
+ - `examples/basic_process.py`: exemplo minimo executavel.
58
+
59
+ ## API principal
60
+
61
+ - `MTChartService.create_process(data)`: normaliza pecas, calcula quantidade total e previsao de saida.
62
+ - `MTChartService.evaluate_reading(process, value)`: classifica leitura como `OK`, `LOW`, `HIGH` ou `N/A`.
63
+ - `MTChartService.search_parts(term)`: consulta o catalogo local de pecas por nome ou PN.
64
+ - `clean_identifier(value)`: remove prefixos comuns como `PN:`, `P/N:`, `LOTE:` e `BATCH:`.
65
+
66
+ ## CLI de demonstracao
67
+
68
+ Depois de instalar localmente, rode:
69
+
70
+ ```powershell
71
+ mtchart-sdk-demo --value 188.7
72
+ ```
73
+
74
+ Ou sem instalar como script:
75
+
76
+ ```powershell
77
+ python -m mtchart_sdk.cli --catalog-db .tmp_demo.db --value 188.7
78
+ ```
79
+
80
+ O comando cria um processo ficticio, avalia a leitura e retorna um JSON com
81
+ status, previsao de saida e resultados do catalogo local.
82
+
83
+ ## Validacao local
84
+
85
+ Para validar uma copia local do SDK durante desenvolvimento:
86
+
87
+ ```powershell
88
+ python tools/validate_package.py
89
+ ```
90
+
91
+ O validador confere metadados, compila os modulos, roda testes e executa o
92
+ exemplo/CLI de demonstracao.
93
+
94
+ ## Status
95
+
96
+ Versao inicial. A API esta pronta para instalacao, testes locais e evolucao incremental.
@@ -0,0 +1,25 @@
1
+ from datetime import datetime
2
+
3
+ from mtchart_sdk import MTChartService, PartItem, PenConfig, ProcessInput
4
+
5
+
6
+ service = MTChartService(catalog_db="example_catalog.db")
7
+
8
+ process = service.create_process(
9
+ ProcessInput(
10
+ report_number="CH-0001",
11
+ project="OS-123",
12
+ process_name="Alivio de fragilizacao",
13
+ oven="Forno 01",
14
+ relief_hours=3,
15
+ pen=PenConfig(id="1", name="Pena 1", stabilization_target=189, low_limit=175.9, high_limit=220),
16
+ items=[PartItem(name="Suporte", pn="PN-001", sn="SN-001", qty=1)],
17
+ started_at=datetime(2026, 6, 28, 8, 0, 0),
18
+ )
19
+ )
20
+
21
+ reading = service.evaluate_reading(process, 188.7, now=datetime(2026, 6, 28, 9, 0, 0))
22
+
23
+ print(process)
24
+ print(reading)
25
+ print(service.search_parts("PN-001"))
@@ -0,0 +1,50 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "mtchart-sdk"
7
+ version = "0.1.0"
8
+ description = "SDK para criar sistemas de monitoramento termico, rastreabilidade e relatorios derivados do MTChart Pro."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "LicenseRef-Proprietary"
12
+ license-files = ["LICENSE.md"]
13
+ authors = [
14
+ { name = "MTD Softwares" }
15
+ ]
16
+ keywords = [
17
+ "monitoramento",
18
+ "processo-termico",
19
+ "rastreabilidade",
20
+ "aviacao",
21
+ "industrial",
22
+ ]
23
+ classifiers = [
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ "Programming Language :: Python :: 3.13",
29
+ "Programming Language :: Python :: 3.14",
30
+ "Topic :: Software Development :: Libraries",
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/Romero-Softwares/mtchart_sdk"
35
+ Repository = "https://github.com/Romero-Softwares/mtchart_sdk"
36
+ Documentation = "https://github.com/Romero-Softwares/mtchart_sdk#readme"
37
+ Issues = "https://github.com/Romero-Softwares/mtchart_sdk/issues"
38
+
39
+ [project.optional-dependencies]
40
+ reports = ["openpyxl>=3.1"]
41
+ dev = ["pytest>=8"]
42
+
43
+ [project.scripts]
44
+ mtchart-sdk-demo = "mtchart_sdk.cli:main"
45
+
46
+ [tool.setuptools.packages.find]
47
+ where = ["src"]
48
+
49
+ [tool.pytest.ini_options]
50
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,31 @@
1
+ from mtchart_sdk.models import (
2
+ PartItem,
3
+ PenConfig,
4
+ ProcessInput,
5
+ ProcessRecord,
6
+ ReadingEvaluation,
7
+ )
8
+ from mtchart_sdk.rules import (
9
+ calculate_exit_timing,
10
+ clean_identifier,
11
+ evaluate_temperature,
12
+ normalize_item,
13
+ total_quantity,
14
+ )
15
+ from mtchart_sdk.service import MTChartService
16
+ from mtchart_sdk.storage import PartsCatalog
17
+
18
+ __all__ = [
19
+ "MTChartService",
20
+ "PartItem",
21
+ "PartsCatalog",
22
+ "PenConfig",
23
+ "ProcessInput",
24
+ "ProcessRecord",
25
+ "ReadingEvaluation",
26
+ "calculate_exit_timing",
27
+ "clean_identifier",
28
+ "evaluate_temperature",
29
+ "normalize_item",
30
+ "total_quantity",
31
+ ]
@@ -0,0 +1,87 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ from datetime import datetime
6
+ from pathlib import Path
7
+
8
+ from mtchart_sdk.models import PartItem, PenConfig, ProcessInput
9
+ from mtchart_sdk.service import MTChartService
10
+
11
+
12
+ def _build_parser() -> argparse.ArgumentParser:
13
+ parser = argparse.ArgumentParser(
14
+ prog="mtchart-sdk-demo",
15
+ description="Executa uma demonstracao local do MTChart SDK.",
16
+ )
17
+ parser.add_argument("--catalog-db", default="mtchart_sdk_demo.db", help="Caminho do catalogo SQLite.")
18
+ parser.add_argument("--report-number", default="CH-DEMO-001", help="Numero do relatorio.")
19
+ parser.add_argument("--project", default="OS-DEMO", help="Projeto ou ordem de servico.")
20
+ parser.add_argument("--process-name", default="Alivio de fragilizacao", help="Nome do processo.")
21
+ parser.add_argument("--oven", default="Forno Demo", help="Nome do forno.")
22
+ parser.add_argument("--relief-hours", type=float, default=3.0, help="Horas de permanencia do processo.")
23
+ parser.add_argument("--pen-id", default="1", help="Identificador da pena.")
24
+ parser.add_argument("--target", type=float, default=189.0, help="Temperatura alvo para iniciar processo.")
25
+ parser.add_argument("--low-limit", type=float, default=175.9, help="Limite inferior de temperatura.")
26
+ parser.add_argument("--high-limit", type=float, default=220.0, help="Limite superior de temperatura.")
27
+ parser.add_argument("--value", default="188.7", help="Leitura de temperatura a avaliar.")
28
+ parser.add_argument("--part-name", default="Suporte Demo", help="Nome da peca.")
29
+ parser.add_argument("--pn", default="PN-DEMO-001", help="Part number da peca.")
30
+ parser.add_argument("--sn", default="SN-DEMO-001", help="Serial number da peca.")
31
+ parser.add_argument("--qty", type=int, default=1, help="Quantidade da peca.")
32
+ return parser
33
+
34
+
35
+ def run_demo(args: argparse.Namespace) -> dict[str, object]:
36
+ service = MTChartService(catalog_db=Path(args.catalog_db))
37
+ started_at = datetime.now().replace(microsecond=0)
38
+ process = service.create_process(
39
+ ProcessInput(
40
+ report_number=args.report_number,
41
+ project=args.project,
42
+ process_name=args.process_name,
43
+ oven=args.oven,
44
+ relief_hours=args.relief_hours,
45
+ pen=PenConfig(
46
+ id=args.pen_id,
47
+ name=f"Pena {args.pen_id}",
48
+ stabilization_target=args.target,
49
+ low_limit=args.low_limit,
50
+ high_limit=args.high_limit,
51
+ ),
52
+ items=[
53
+ PartItem(
54
+ name=args.part_name,
55
+ pn=args.pn,
56
+ sn=args.sn,
57
+ qty=args.qty,
58
+ )
59
+ ],
60
+ started_at=started_at,
61
+ )
62
+ )
63
+ reading = service.evaluate_reading(process, args.value, now=started_at)
64
+ return {
65
+ "report_number": process.report_number,
66
+ "project": process.project,
67
+ "expected_exit_at": process.expected_exit_at.isoformat(),
68
+ "total_quantity": process.total_quantity,
69
+ "reading": {
70
+ "value": reading.value,
71
+ "status": reading.status,
72
+ "can_start_process": reading.can_start_process,
73
+ "remaining_seconds": reading.remaining_seconds,
74
+ },
75
+ "catalog_matches": service.search_parts(args.pn, limit=5),
76
+ }
77
+
78
+
79
+ def main(argv: list[str] | None = None) -> int:
80
+ parser = _build_parser()
81
+ result = run_demo(parser.parse_args(argv))
82
+ print(json.dumps(result, indent=2, ensure_ascii=False))
83
+ return 0
84
+
85
+
86
+ if __name__ == "__main__":
87
+ raise SystemExit(main())
@@ -0,0 +1,63 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from datetime import datetime
5
+ from typing import Any
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class PartItem:
10
+ name: str = ""
11
+ pn: str = ""
12
+ sn: str = ""
13
+ qty: int = 1
14
+ project: str = ""
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class PenConfig:
19
+ id: str
20
+ name: str = ""
21
+ stabilization_target: float | None = None
22
+ low_limit: float | None = None
23
+ high_limit: float | None = None
24
+ tolerance: float = 0.5
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class ProcessInput:
29
+ report_number: str
30
+ project: str
31
+ process_name: str
32
+ oven: str
33
+ relief_hours: float
34
+ pen: PenConfig
35
+ items: list[PartItem | dict[str, Any]] = field(default_factory=list)
36
+ started_at: datetime | None = None
37
+ metadata: dict[str, Any] = field(default_factory=dict)
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class ProcessRecord:
42
+ report_number: str
43
+ project: str
44
+ process_name: str
45
+ oven: str
46
+ relief_hours: float
47
+ pen: PenConfig
48
+ items: list[PartItem]
49
+ started_at: datetime
50
+ expected_exit_at: datetime
51
+ total_quantity: int
52
+ metadata: dict[str, Any] = field(default_factory=dict)
53
+
54
+
55
+ @dataclass(frozen=True)
56
+ class ReadingEvaluation:
57
+ value: float | None
58
+ status: str
59
+ is_numeric: bool
60
+ is_within_limits: bool
61
+ is_low_temperature: bool
62
+ can_start_process: bool
63
+ remaining_seconds: float
@@ -0,0 +1,101 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from datetime import datetime, timedelta
5
+ from typing import Any
6
+
7
+ from mtchart_sdk.models import PartItem, PenConfig, ReadingEvaluation
8
+
9
+
10
+ def to_float_or_none(value: Any) -> float | None:
11
+ try:
12
+ if value in ("", None):
13
+ return None
14
+ return float(value)
15
+ except (TypeError, ValueError):
16
+ return None
17
+
18
+
19
+ def clean_identifier(value: Any) -> str:
20
+ text = str(value or "").strip()
21
+ return re.sub(r"^\s*(PN|P/N|LOTE|BATCH)\s*:\s*", "", text, flags=re.IGNORECASE).strip()
22
+
23
+
24
+ def normalize_item(item: PartItem | dict[str, Any] | str) -> PartItem:
25
+ if isinstance(item, PartItem):
26
+ return item
27
+ if isinstance(item, dict):
28
+ try:
29
+ qty = max(1, int(str(item.get("qty", 1)).strip() or "1"))
30
+ except (TypeError, ValueError):
31
+ qty = 1
32
+ return PartItem(
33
+ name=str(item.get("name", item.get("nome", ""))).strip(),
34
+ pn=clean_identifier(item.get("pn", "")),
35
+ sn=str(item.get("sn", "")).strip(),
36
+ qty=qty,
37
+ project=str(item.get("project", item.get("projeto", ""))).strip(),
38
+ )
39
+ return PartItem(sn=str(item or "").strip())
40
+
41
+
42
+ def total_quantity(items: list[PartItem | dict[str, Any] | str]) -> int:
43
+ return sum(normalize_item(item).qty for item in items or [])
44
+
45
+
46
+ def calculate_exit_timing(started_at: datetime, relief_hours: float, now: datetime | None = None) -> tuple[datetime, float]:
47
+ now = now or datetime.now()
48
+ expected_exit_at = started_at + timedelta(hours=float(relief_hours or 0))
49
+ return expected_exit_at, (expected_exit_at - now).total_seconds()
50
+
51
+
52
+ def evaluate_temperature(
53
+ value: Any,
54
+ pen: PenConfig,
55
+ started_at: datetime,
56
+ relief_hours: float,
57
+ now: datetime | None = None,
58
+ ) -> ReadingEvaluation:
59
+ numeric_value = to_float_or_none(value)
60
+ expected_exit_at, remaining_seconds = calculate_exit_timing(started_at, relief_hours, now)
61
+ del expected_exit_at
62
+
63
+ if numeric_value is None:
64
+ return ReadingEvaluation(
65
+ value=None,
66
+ status="N/A",
67
+ is_numeric=False,
68
+ is_within_limits=False,
69
+ is_low_temperature=False,
70
+ can_start_process=False,
71
+ remaining_seconds=remaining_seconds,
72
+ )
73
+
74
+ low_limit = pen.low_limit
75
+ high_limit = pen.high_limit
76
+ has_low = low_limit is not None
77
+ has_high = high_limit is not None
78
+ is_low = has_low and numeric_value < float(low_limit)
79
+ is_high = has_high and numeric_value > float(high_limit)
80
+ is_within = not is_low and not is_high
81
+ target = pen.stabilization_target
82
+ can_start = target is not None and numeric_value >= float(target) - float(pen.tolerance)
83
+
84
+ if is_low:
85
+ status = "LOW"
86
+ elif is_high:
87
+ status = "HIGH"
88
+ elif is_within:
89
+ status = "OK"
90
+ else:
91
+ status = "UNKNOWN"
92
+
93
+ return ReadingEvaluation(
94
+ value=numeric_value,
95
+ status=status,
96
+ is_numeric=True,
97
+ is_within_limits=is_within,
98
+ is_low_temperature=is_low,
99
+ can_start_process=can_start,
100
+ remaining_seconds=remaining_seconds,
101
+ )
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+ from pathlib import Path
5
+
6
+ from mtchart_sdk.models import PartItem, ProcessInput, ProcessRecord, ReadingEvaluation
7
+ from mtchart_sdk.rules import calculate_exit_timing, evaluate_temperature, normalize_item, total_quantity
8
+ from mtchart_sdk.storage import PartsCatalog
9
+
10
+
11
+ class MTChartService:
12
+ def __init__(self, catalog_db: str | Path | None = None) -> None:
13
+ self.catalog = PartsCatalog(catalog_db or "mtchart_sdk.db")
14
+
15
+ def create_process(self, data: ProcessInput) -> ProcessRecord:
16
+ started_at = data.started_at or datetime.now()
17
+ items = [normalize_item(item) for item in data.items]
18
+ for item in items:
19
+ if item.name and item.pn:
20
+ self.catalog.save(item.name, item.pn)
21
+ expected_exit_at, _remaining = calculate_exit_timing(started_at, data.relief_hours, started_at)
22
+ return ProcessRecord(
23
+ report_number=data.report_number,
24
+ project=data.project,
25
+ process_name=data.process_name,
26
+ oven=data.oven,
27
+ relief_hours=float(data.relief_hours or 0),
28
+ pen=data.pen,
29
+ items=items,
30
+ started_at=started_at,
31
+ expected_exit_at=expected_exit_at,
32
+ total_quantity=total_quantity(items),
33
+ metadata=dict(data.metadata or {}),
34
+ )
35
+
36
+ def evaluate_reading(
37
+ self,
38
+ process: ProcessRecord,
39
+ value: float | str | None,
40
+ now: datetime | None = None,
41
+ ) -> ReadingEvaluation:
42
+ return evaluate_temperature(
43
+ value=value,
44
+ pen=process.pen,
45
+ started_at=process.started_at,
46
+ relief_hours=process.relief_hours,
47
+ now=now,
48
+ )
49
+
50
+ def search_parts(self, term: str = "", limit: int = 100) -> list[dict[str, object]]:
51
+ return self.catalog.search(term, limit)
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ import sqlite3
4
+ from contextlib import contextmanager
5
+ from datetime import datetime
6
+ from pathlib import Path
7
+ from typing import Iterator
8
+
9
+ from mtchart_sdk.rules import clean_identifier
10
+
11
+
12
+ class PartsCatalog:
13
+ def __init__(self, db_path: str | Path = "mtchart_sdk.db") -> None:
14
+ self.db_path = Path(db_path)
15
+ if self.db_path.parent != Path("."):
16
+ self.db_path.parent.mkdir(parents=True, exist_ok=True)
17
+ self.init_db()
18
+
19
+ @contextmanager
20
+ def connect(self) -> Iterator[sqlite3.Connection]:
21
+ conn = sqlite3.connect(self.db_path)
22
+ try:
23
+ yield conn
24
+ conn.commit()
25
+ except Exception:
26
+ conn.rollback()
27
+ raise
28
+ finally:
29
+ conn.close()
30
+
31
+ def init_db(self) -> None:
32
+ with self.connect() as conn:
33
+ conn.execute(
34
+ """
35
+ CREATE TABLE IF NOT EXISTS catalog_parts (
36
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
37
+ name TEXT NOT NULL,
38
+ pn TEXT NOT NULL,
39
+ name_norm TEXT NOT NULL,
40
+ pn_norm TEXT NOT NULL,
41
+ use_count INTEGER DEFAULT 1,
42
+ last_used_at TEXT NOT NULL,
43
+ UNIQUE(name_norm, pn_norm)
44
+ )
45
+ """
46
+ )
47
+ conn.execute("CREATE INDEX IF NOT EXISTS idx_catalog_parts_name ON catalog_parts(name_norm)")
48
+ conn.execute("CREATE INDEX IF NOT EXISTS idx_catalog_parts_pn ON catalog_parts(pn_norm)")
49
+
50
+ def save(self, name: str, pn: str, increment: bool = True) -> None:
51
+ name = clean_identifier(name)
52
+ pn = clean_identifier(pn)
53
+ if not name or not pn:
54
+ raise ValueError("name and pn are required")
55
+ name_norm = name.upper()
56
+ pn_norm = pn.upper()
57
+ increment_by = 1 if increment else 0
58
+ now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
59
+ with self.connect() as conn:
60
+ conn.execute(
61
+ """
62
+ INSERT INTO catalog_parts (name, pn, name_norm, pn_norm, use_count, last_used_at)
63
+ VALUES (?, ?, ?, ?, 1, ?)
64
+ ON CONFLICT(name_norm, pn_norm) DO UPDATE SET
65
+ name = excluded.name,
66
+ pn = excluded.pn,
67
+ use_count = catalog_parts.use_count + ?,
68
+ last_used_at = excluded.last_used_at
69
+ """,
70
+ (name, pn, name_norm, pn_norm, now, increment_by),
71
+ )
72
+
73
+ def search(self, term: str = "", limit: int = 100) -> list[dict[str, object]]:
74
+ term_norm = clean_identifier(term).upper()
75
+ limit = max(1, min(1000, int(limit or 100)))
76
+ query = """
77
+ SELECT id, name, pn, use_count, last_used_at
78
+ FROM catalog_parts
79
+ """
80
+ params: tuple[object, ...]
81
+ if term_norm:
82
+ query += " WHERE name_norm LIKE ? OR pn_norm LIKE ?"
83
+ params = (f"%{term_norm}%", f"%{term_norm}%", limit)
84
+ else:
85
+ params = (limit,)
86
+ query += " ORDER BY use_count DESC, id DESC LIMIT ?"
87
+ with self.connect() as conn:
88
+ rows = conn.execute(query, params).fetchall()
89
+ return [
90
+ {
91
+ "id": row[0],
92
+ "name": row[1],
93
+ "pn": row[2],
94
+ "use_count": row[3],
95
+ "last_used_at": row[4],
96
+ }
97
+ for row in rows
98
+ ]
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: mtchart-sdk
3
+ Version: 0.1.0
4
+ Summary: SDK para criar sistemas de monitoramento termico, rastreabilidade e relatorios derivados do MTChart Pro.
5
+ Author: MTD Softwares
6
+ License-Expression: LicenseRef-Proprietary
7
+ Project-URL: Homepage, https://github.com/Romero-Softwares/mtchart_sdk
8
+ Project-URL: Repository, https://github.com/Romero-Softwares/mtchart_sdk
9
+ Project-URL: Documentation, https://github.com/Romero-Softwares/mtchart_sdk#readme
10
+ Project-URL: Issues, https://github.com/Romero-Softwares/mtchart_sdk/issues
11
+ Keywords: monitoramento,processo-termico,rastreabilidade,aviacao,industrial
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE.md
22
+ Provides-Extra: reports
23
+ Requires-Dist: openpyxl>=3.1; extra == "reports"
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # MTChart SDK
29
+
30
+ SDK inicial para desenvolvedores criarem sistemas semelhantes ao MTChart Pro usando classes e metodos reutilizaveis.
31
+
32
+ Esta primeira versao fica separada do aplicativo desktop e entrega um nucleo limpo para:
33
+
34
+ - cadastrar processos termicos;
35
+ - validar leituras de temperatura;
36
+ - calcular previsao de saida;
37
+ - registrar catalogo local de pecas e PN;
38
+ - montar dados de rastreabilidade sem depender da interface Flet.
39
+
40
+ ## Instalacao
41
+
42
+ ```powershell
43
+ python -m pip install mtchart-sdk
44
+ ```
45
+
46
+ ## Exemplo rapido
47
+
48
+ ```python
49
+ from datetime import datetime
50
+
51
+ from mtchart_sdk import MTChartService, PartItem, PenConfig, ProcessInput
52
+
53
+ service = MTChartService()
54
+
55
+ process = service.create_process(
56
+ ProcessInput(
57
+ report_number="CH-0001",
58
+ project="OS-123",
59
+ process_name="Alivio de fragilizacao",
60
+ oven="Forno 01",
61
+ relief_hours=3,
62
+ pen=PenConfig(id="1", name="Pena 1", stabilization_target=189, low_limit=175.9),
63
+ items=[
64
+ PartItem(name="Suporte", pn="PN-001", sn="SN-001", qty=1),
65
+ ],
66
+ started_at=datetime(2026, 6, 28, 8, 0, 0),
67
+ )
68
+ )
69
+
70
+ reading = service.evaluate_reading(process, value=188.7)
71
+
72
+ print(process.report_number)
73
+ print(reading.status)
74
+ print(reading.can_start_process)
75
+ ```
76
+
77
+ ## Estrutura
78
+
79
+ - `mtchart_sdk.models`: modelos de dados do processo.
80
+ - `mtchart_sdk.rules`: regras puras de temperatura e tempo.
81
+ - `mtchart_sdk.storage`: catalogo SQLite local de pecas.
82
+ - `mtchart_sdk.service`: fachada principal para uso por outros sistemas.
83
+ - `mtchart_sdk.cli`: demonstracao de linha de comando para validar instalacao.
84
+ - `examples/basic_process.py`: exemplo minimo executavel.
85
+
86
+ ## API principal
87
+
88
+ - `MTChartService.create_process(data)`: normaliza pecas, calcula quantidade total e previsao de saida.
89
+ - `MTChartService.evaluate_reading(process, value)`: classifica leitura como `OK`, `LOW`, `HIGH` ou `N/A`.
90
+ - `MTChartService.search_parts(term)`: consulta o catalogo local de pecas por nome ou PN.
91
+ - `clean_identifier(value)`: remove prefixos comuns como `PN:`, `P/N:`, `LOTE:` e `BATCH:`.
92
+
93
+ ## CLI de demonstracao
94
+
95
+ Depois de instalar localmente, rode:
96
+
97
+ ```powershell
98
+ mtchart-sdk-demo --value 188.7
99
+ ```
100
+
101
+ Ou sem instalar como script:
102
+
103
+ ```powershell
104
+ python -m mtchart_sdk.cli --catalog-db .tmp_demo.db --value 188.7
105
+ ```
106
+
107
+ O comando cria um processo ficticio, avalia a leitura e retorna um JSON com
108
+ status, previsao de saida e resultados do catalogo local.
109
+
110
+ ## Validacao local
111
+
112
+ Para validar uma copia local do SDK durante desenvolvimento:
113
+
114
+ ```powershell
115
+ python tools/validate_package.py
116
+ ```
117
+
118
+ O validador confere metadados, compila os modulos, roda testes e executa o
119
+ exemplo/CLI de demonstracao.
120
+
121
+ ## Status
122
+
123
+ Versao inicial. A API esta pronta para instalacao, testes locais e evolucao incremental.
@@ -0,0 +1,18 @@
1
+ LICENSE.md
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ examples/basic_process.py
6
+ src/mtchart_sdk/__init__.py
7
+ src/mtchart_sdk/cli.py
8
+ src/mtchart_sdk/models.py
9
+ src/mtchart_sdk/rules.py
10
+ src/mtchart_sdk/service.py
11
+ src/mtchart_sdk/storage.py
12
+ src/mtchart_sdk.egg-info/PKG-INFO
13
+ src/mtchart_sdk.egg-info/SOURCES.txt
14
+ src/mtchart_sdk.egg-info/dependency_links.txt
15
+ src/mtchart_sdk.egg-info/entry_points.txt
16
+ src/mtchart_sdk.egg-info/requires.txt
17
+ src/mtchart_sdk.egg-info/top_level.txt
18
+ tests/test_sdk.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mtchart-sdk-demo = mtchart_sdk.cli:main
@@ -0,0 +1,6 @@
1
+
2
+ [dev]
3
+ pytest>=8
4
+
5
+ [reports]
6
+ openpyxl>=3.1
@@ -0,0 +1 @@
1
+ mtchart_sdk
@@ -0,0 +1,121 @@
1
+ from datetime import datetime
2
+
3
+ from mtchart_sdk import MTChartService, PartItem, PenConfig, ProcessInput, clean_identifier, normalize_item
4
+ from mtchart_sdk.cli import main, run_demo
5
+
6
+
7
+ def test_create_process_and_evaluate_reading(tmp_path):
8
+ service = MTChartService(catalog_db=tmp_path / "catalog.db")
9
+ process = service.create_process(
10
+ ProcessInput(
11
+ report_number="CH-0001",
12
+ project="OS-1",
13
+ process_name="Alivio",
14
+ oven="Forno 01",
15
+ relief_hours=2,
16
+ pen=PenConfig(id="1", stabilization_target=189, low_limit=175.9, high_limit=220),
17
+ items=[PartItem(name="Peca", pn="PN-1", sn="SN-1", qty=2)],
18
+ started_at=datetime(2026, 6, 28, 8, 0, 0),
19
+ )
20
+ )
21
+
22
+ reading = service.evaluate_reading(process, 188.6, now=datetime(2026, 6, 28, 9, 0, 0))
23
+
24
+ assert process.total_quantity == 2
25
+ assert process.expected_exit_at == datetime(2026, 6, 28, 10, 0, 0)
26
+ assert reading.status == "OK"
27
+ assert reading.can_start_process is True
28
+ assert service.search_parts("PN-1")[0]["name"] == "Peca"
29
+
30
+
31
+ def test_temperature_statuses_are_classified(tmp_path):
32
+ service = MTChartService(catalog_db=tmp_path / "catalog.db")
33
+ process = service.create_process(
34
+ ProcessInput(
35
+ report_number="CH-0002",
36
+ project="OS-2",
37
+ process_name="Teste",
38
+ oven="Forno 02",
39
+ relief_hours=1,
40
+ pen=PenConfig(id="2", stabilization_target=180, low_limit=170, high_limit=200),
41
+ started_at=datetime(2026, 6, 28, 8, 0, 0),
42
+ )
43
+ )
44
+
45
+ assert service.evaluate_reading(process, 169).status == "LOW"
46
+ assert service.evaluate_reading(process, 201).status == "HIGH"
47
+ assert service.evaluate_reading(process, "").status == "N/A"
48
+
49
+
50
+ def test_catalog_reuses_part_and_increments_count(tmp_path):
51
+ service = MTChartService(catalog_db=tmp_path / "catalog.db")
52
+ data = ProcessInput(
53
+ report_number="CH-0003",
54
+ project="OS-3",
55
+ process_name="Teste",
56
+ oven="Forno 03",
57
+ relief_hours=1,
58
+ pen=PenConfig(id="3"),
59
+ items=[
60
+ {"name": "Suporte", "pn": "PN: ABC-1"},
61
+ {"name": "Suporte", "pn": "ABC-1"},
62
+ ],
63
+ )
64
+
65
+ service.create_process(data)
66
+ result = service.search_parts("ABC-1")
67
+
68
+ assert len(result) == 1
69
+ assert result[0]["use_count"] == 2
70
+
71
+
72
+ def test_normalize_item_accepts_portuguese_keys():
73
+ item = normalize_item({"nome": "Peca", "pn": "P/N: X-1", "projeto": "OS-4", "qty": "3"})
74
+
75
+ assert item.name == "Peca"
76
+ assert item.pn == "X-1"
77
+ assert item.project == "OS-4"
78
+ assert item.qty == 3
79
+
80
+
81
+ def test_clean_identifier_removes_common_labels():
82
+ assert clean_identifier("PN: ABC-123") == "ABC-123"
83
+ assert clean_identifier("BATCH: L-1") == "L-1"
84
+ assert clean_identifier("P/N: XPTO") == "XPTO"
85
+
86
+
87
+ def test_cli_demo_returns_process_payload(tmp_path):
88
+ parser_args = [
89
+ "--catalog-db",
90
+ str(tmp_path / "demo.db"),
91
+ "--report-number",
92
+ "CH-CLI",
93
+ "--value",
94
+ "188.7",
95
+ "--pn",
96
+ "PN-CLI",
97
+ ]
98
+
99
+ assert main(parser_args) == 0
100
+
101
+
102
+ def test_cli_run_demo_payload_can_be_consumed(tmp_path):
103
+ from mtchart_sdk.cli import _build_parser
104
+
105
+ args = _build_parser().parse_args(
106
+ [
107
+ "--catalog-db",
108
+ str(tmp_path / "demo.db"),
109
+ "--report-number",
110
+ "CH-CLI",
111
+ "--value",
112
+ "188.7",
113
+ "--pn",
114
+ "PN-CLI",
115
+ ]
116
+ )
117
+ result = run_demo(args)
118
+
119
+ assert result["report_number"] == "CH-CLI"
120
+ assert result["reading"]["status"] == "OK"
121
+ assert result["catalog_matches"][0]["pn"] == "PN-CLI"