API local desenvolvida em C#/.NET Framework para fazer a ponte entre uma aplicação externa e o software Bluehill Universal da Instron.
A aplicação roda como um .exe SelfHost, sem depender do IIS, mantendo a conexão com o Bluehill ativa enquanto o programa estiver aberto.
O objetivo deste projeto é permitir que uma aplicação externa, como um sistema em PHP, consiga comunicar com o Bluehill Universal através de endpoints HTTP.
A API permite:
- verificar se a aplicação está online;
- conectar ao Bluehill;
- obter o estado atual do Bluehill;
- criar amostras;
- iniciar e parar testes;
- consultar resultados;
- guardar e fechar amostras;
- consultar medições;
- gerar logs de teste.
- C#
- .NET Framework 4.7.2
- ASP.NET Web API
- OWIN SelfHost
- Bluehill.API.dll
- Postman
- PHP/cURL para consumo externo
Fluxo principal:
Aplicação PHP / Postman / Frontend
↓
InstronBridgeSelfHost.exe
↓
Bluehill.API.dll
↓
Bluehill Universal
↓
Máquina Instron
O .exe cria um pequeno servidor HTTP local na porta 9000.
Base URL padrão:
http://localhost:9000InstronBridgeSelfHost
│
├── App_Start
│ └── WebApiConfig.cs
│
├── Callbacks
│ └── InstronCallback.cs
│
├── Controllers
│ └── InstronController.cs
│
├── InstronLogs
│ └── Logger.cs
│
├── Models
│ ├── CreateSampleRequest.cs
│ ├── SaveSampleRequest.cs
│ ├── PedidoBluehillDto.cs
│ └── InstronServiceState.cs
│
├── Services
│ └── InstronService.cs
│
├── Program.cs
├── Startup.cs
├── App.config
└── packages.config
A máquina onde o .exe será executado precisa ter:
Bluehill Universal instalado
Bluehill.API.dll disponível
.NET Framework 4.7.2 ou superior
Permissão para executar o Bluehill
Permissão para criar ficheiros de log
Também é importante executar o .exe como administrador, principalmente durante os testes iniciais.
No Visual Studio:
Build → Configuration Manager
Selecionar:
Release
x86
Depois executar:
Build → Rebuild Solution
O projeto deve ser compilado em x86, porque a Bluehill.API.dll trabalha com arquitetura x86.
Após compilar, copiar todo o conteúdo da pasta:
bin\x86\Release
Para a máquina da Instron, por exemplo:
C:\InstronBridgeSelfHost
A estrutura final pode ficar assim:
C:\InstronBridgeSelfHost
│
├── InstronBridgeSelfHost.exe
├── InstronBridgeSelfHost.exe.config
├── Bluehill.API.dll
├── Microsoft.Owin.dll
├── Newtonsoft.Json.dll
├── outras DLLs...
└── InstronLogs
Executar:
InstronBridgeSelfHost.exe
De preferência:
Botão direito → Executar como administrador
A consola deve mostrar algo parecido com:
==========================================
Instron Bridge API Self Host iniciado
==========================================
URL:
http://localhost:9000/
Importante: a janela da consola precisa permanecer aberta. Ela é o servidor da API.
Os logs são guardados dentro da pasta onde o .exe está a ser executado.
Exemplo:
C:\InstronBridgeSelfHost\InstronLogs\logs.txt
Durante o desenvolvimento, o caminho pode ser:
bin\x86\Release\InstronLogs\logs.txt
Exemplo de log:
2026-05-19 14:30:22 [INFO] SelfHost iniciado com sucesso.
2026-05-19 14:31:10 [INFO] Conectado ao Bluehill com sucesso.
Base URL:
http://localhost:9000/api/instronVerifica se a API está online e mostra informações sobre a conexão.
GET /healthGET http://localhost:9000/api/instron/health{
"status": "online",
"connected": true,
"lastState": "ReadyToStartTest",
"lastStatusCode": null,
"lastStatusMessage": "Conectado ao Bluehill com sucesso."
}Este endpoint serve para verificar:
- se a API está ligada;
- se existe conexão com o Bluehill;
- qual foi o último estado conhecido;
- qual foi a última mensagem recebida.
Inicia o Bluehill, estabelece conexão com a API do Bluehill e mantém a referência em memória.
POST /connectPOST http://localhost:9000/api/instron/connectNão precisa de body.
{
"message": "Conectado ao Bluehill com sucesso."
}Este endpoint pode ser usado para forcar a abertura/conexao manual durante testes.
No fluxo operacional, os endpoints /state, /results, /results/formatted e /measurement
tentam conectar automaticamente ao Bluehill quando ainda nao existe uma sessao valida.
Retorna o estado atual do Bluehill.
GET /stateGET http://localhost:9000/api/instron/state{
"state": "ReadyToStartTest"
}Permite saber em que estado o Bluehill se encontra no momento.
Exemplos de estados possíveis:
BluehillStarting
BluehillHome
SampleOpened
ReadyToStartTest
Running
Calculating
Cria uma nova amostra no Bluehill a partir de um ficheiro de método.
POST /create-samplePOST http://localhost:9000/api/instron/create-sample{
"methodFilePath": "C:\\Users\\Public\\Documents\\Instron\\Bluehill Universal\\Templates\\metodo.im_tens"
}{
"message": "Amostra criada com sucesso.",
"result": "NoError"
}Este endpoint é usado quando a aplicação externa precisa criar uma nova amostra no Bluehill com base num método existente.
Inicia o teste atual no Bluehill.
POST /start-testPOST http://localhost:9000/api/instron/start-testNão precisa de body.
{
"message": "Teste iniciado com sucesso.",
"result": "NoError"
}Este endpoint pode movimentar a máquina física. Usar apenas com operador presente e com o ensaio preparado.
Para o teste em execução.
POST /stop-testPOST http://localhost:9000/api/instron/stop-testNão precisa de body.
{
"message": "Teste parado com sucesso.",
"result": "NoError"
}Permite interromper um teste em andamento através da API.
Obtém os dados da tabela de resultados e estatísticas da amostra atualmente aberta no Bluehill.
GET /results?tableNumber=1GET http://localhost:9000/api/instron/results?tableNumber=1{
"tableNumber": 1,
"data": [
["Specimen", "Peak Load", "Extension"],
[1, 520.4, 12.8]
]
}Este endpoint não devolve o histórico completo da máquina. Ele devolve os dados da tabela de resultados da amostra atualmente aberta no Bluehill.
Se retornar:
{
"tableNumber": 1,
"data": null
}pode significar que:
- não existe amostra aberta;
- a tabela 1 está vazia;
- o teste ainda não gerou resultados;
- os resultados ainda não foram calculados;
- a amostra aberta não possui resultados gravados.
Obtem os mesmos resultados da tabela, mas devolve os dados em formato pronto para consumo por uma tabela no frontend.
GET /results/formatted?tableNumber=1GET http://localhost:9000/api/instron/results/formatted?tableNumber=1{
"tableNumber": 1,
"headers": ["Specimen", "Peak Load", "Extension"],
"rows": [
{
"Specimen": 1,
"Peak Load": 520.4,
"Extension": 12.8
}
]
}Este endpoint tambem tenta conectar/reconectar automaticamente ao Bluehill antes de consultar a tabela.
Guarda a amostra atual.
POST /save-samplePOST http://localhost:9000/api/instron/save-sample{
"filePath": "C:\\Users\\Public\\Documents\\Instron\\Bluehill Universal\\Samples\\amostra1.is_tens"
}{
"message": "Amostra guardada com sucesso.",
"result": "NoError"
}Se o filePath for nulo, o Bluehill pode usar o caminho padrão.
Fecha a amostra atualmente aberta no Bluehill.
POST /close-samplePOST http://localhost:9000/api/instron/close-sampleNão precisa de body.
{
"message": "Amostra fechada com sucesso.",
"result": "NoError"
}Obtém uma medição específica do Bluehill.
GET /measurement?measurementName=NOME&unit=UNIDADEGET http://localhost:9000/api/instron/measurement?measurementName=Load&unit=Newtons{
"measurement": "Load",
"unit": "Newtons",
"value": 120.45
}O nome da medição e a unidade precisam existir no Bluehill.
Fecha a conexao WCF da API e tenta encerrar o processo Bluehill. Se a janela nao responder, o processo e finalizado para evitar sessoes presas no Task Manager.
POST /disconnectPOST http://localhost:9000/api/instron/disconnectNão precisa de body.
{
"message": "Conexao encerrada e processo Bluehill finalizado."
}Este endpoint e util para testes e manutencao. No frontend operacional, nao e necessario expor botao de conectar/desconectar.
Endpoint simples para testar envio de JSON.
POST /testePOST http://localhost:9000/api/instron/teste{
"nome": "Teste",
"nif": "123456789",
"email": "[email protected]"
}{
"mensagem": "Dados recebidos com sucesso",
"dados": {
"nome": "Teste",
"nif": "123456789",
"email": "[email protected]"
}
}Endpoint usado para testar a escrita de logs.
POST /testeLogsPOST http://localhost:9000/api/instron/testeLogs{
"nome": "Leonardo",
"nif": "123456789",
"email": "[email protected]"
}{
"sucesso": true,
"mensagem": "Log criado com sucesso"
}Fluxo básico:
1. Abrir InstronBridgeSelfHost.exe
2. GET /health
3. GET /state
4. POST /create-sample
5. POST /start-test
6. GET /results/formatted?tableNumber=1
7. POST /save-sample
8. POST /close-sample
Para testes sem movimentar a máquina:
1. Abrir InstronBridgeSelfHost.exe
2. Abrir manualmente no Bluehill uma amostra já existente com resultados
3. GET /results/formatted?tableNumber=1
A aplicação PHP pode consumir a API usando cURL.
Se o PHP estiver na mesma máquina da Instron:
$baseUrl = "http://localhost:9000/api/instron";Se o PHP estiver noutra máquina da mesma rede:
$baseUrl = "http://IP-DA-MAQUINA-INSTRON:9000/api/instron";Exemplo:
$baseUrl = "http://172.21.0.194:9000/api/instron";<?php
$url = "http://localhost:9000/api/instron/health";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo "Erro cURL: " . curl_error($ch);
} else {
$data = json_decode($response, true);
echo "<pre>";
print_r($data);
echo "</pre>";
}
curl_close($ch);<?php
$url = "http://localhost:9000/api/instron/connect";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo "Erro cURL: " . curl_error($ch);
} else {
echo $response;
}
curl_close($ch);<?php
$url = "http://localhost:9000/api/instron/create-sample";
$body = [
"methodFilePath" => "C:\\Users\\Public\\Documents\\Instron\\Bluehill Universal\\Templates\\metodo.im_tens"
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo "Erro cURL: " . curl_error($ch);
} else {
$data = json_decode($response, true);
echo "<pre>";
print_r($data);
echo "</pre>";
}
curl_close($ch);<?php
$url = "http://localhost:9000/api/instron/start-test";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo "Erro cURL: " . curl_error($ch);
} else {
echo $response;
}
curl_close($ch);<?php
$tableNumber = 1;
$url = "http://localhost:9000/api/instron/results?tableNumber=" . $tableNumber;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo "Erro cURL: " . curl_error($ch);
} else {
$data = json_decode($response, true);
echo "<pre>";
print_r($data);
echo "</pre>";
}
curl_close($ch);<?php
$measurementName = urlencode("Load");
$unit = urlencode("Newtons");
$url = "http://localhost:9000/api/instron/measurement?measurementName={$measurementName}&unit={$unit}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo "Erro cURL: " . curl_error($ch);
} else {
$data = json_decode($response, true);
echo "<pre>";
print_r($data);
echo "</pre>";
}
curl_close($ch);<?php
class InstronApiClient
{
private string $baseUrl;
public function __construct(string $baseUrl = "http://localhost:9000/api/instron")
{
$this->baseUrl = rtrim($baseUrl, "/");
}
private function request(string $method, string $endpoint, array $body = null)
{
$url = $this->baseUrl . $endpoint;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if ($method === "POST") {
curl_setopt($ch, CURLOPT_POST, true);
}
if ($body !== null) {
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
return json_decode($response, true);
}
public function health()
{
return $this->request("GET", "/health");
}
public function connect()
{
return $this->request("POST", "/connect");
}
public function state()
{
return $this->request("GET", "/state");
}
public function createSample(string $methodFilePath)
{
return $this->request("POST", "/create-sample", [
"methodFilePath" => $methodFilePath
]);
}
public function startTest()
{
return $this->request("POST", "/start-test");
}
public function stopTest()
{
return $this->request("POST", "/stop-test");
}
public function results(int $tableNumber = 1)
{
return $this->request("GET", "/results?tableNumber=" . $tableNumber);
}
public function saveSample(string $filePath)
{
return $this->request("POST", "/save-sample", [
"filePath" => $filePath
]);
}
public function closeSample()
{
return $this->request("POST", "/close-sample");
}
public function measurement(string $measurementName, string $unit)
{
return $this->request(
"GET",
"/measurement?measurementName=" . urlencode($measurementName) . "&unit=" . urlencode($unit)
);
}
public function disconnect()
{
return $this->request("POST", "/disconnect");
}
}<?php
require_once "InstronApiClient.php";
$instron = new InstronApiClient();
try {
$health = $instron->health();
print_r($health);
$connect = $instron->connect();
print_r($connect);
$state = $instron->state();
print_r($state);
} catch (Exception $e) {
echo "Erro: " . $e->getMessage();
}Esta API controla ou pode controlar uma máquina física. Portanto:
Não expor esta API diretamente à internet
Executar apenas em rede local ou ambiente controlado
Restringir a porta 9000 na firewall
Permitir apenas máquinas autorizadas
Adicionar autenticação em versões futuras
Manter operador presente durante testes reais
Usar start-test apenas com segurança confirmada
Possíveis causas:
Amostra não está aberta
Tabela de resultados vazia
Teste ainda não foi executado
Resultados ainda não foram calculados
Número da tabela incorreto
Solução:
Abrir uma amostra com resultados ou executar um teste real antes de consultar /results.
Erro típico:
An attempt was made to load a program with an incorrect format.
Solução:
Compilar o projeto em x86.
Verificar:
Bluehill Universal instalado
Bluehill.API.dll junto ao .exe
Caminho do Bluehill.exe correto
Executar como administrador
Fechar processos antigos do Bluehill
Verificar:
Se o .exe está aberto
Se a firewall permite a porta 9000
Se outro processo já está usando a porta
Este projeto foi estruturado como SelfHost porque o IIS não manteve corretamente a conexão persistente com o Bluehill entre requests.
Com o SelfHost, a conexão permanece viva enquanto o .exe estiver aberto, tornando a solução mais adequada para integração com software industrial baseado em WCF/Named Pipes.