Fluke MET/TEAM¶
Integration with Fluke MET/TEAM calibration management software.
Overview¶
calServer V2 offers integration with Fluke MET/TEAM, a widely used calibration management software. The integration enables data exchange between both systems.
Features¶
| Feature | Description |
|---|---|
| Data Import | Import device and calibration data from MET/TEAM |
| Initial migration | Take over the whole MET/TEAM stock once (metteam:migrate) |
| Data Export | Export calServer data to MET/TEAM |
| Field Mapping | Configurable mapping of data fields |
| Synchronization | Periodic data reconciliation |
Setup¶
Prerequisites¶
MET/TEAM version and access
The integration requires MET/TEAM 3.0 or higher. calServer talks to the
MET/TEAM database on Microsoft SQL Server directly; it needs a network
connection to the SQL Server and a SQL login on the MET/TEAM database.
For the initial migration, read rights on the tables
and the execute right on MET/TEAM's own helper functions (udf_GET_…) are
enough.
The ongoing sync creates procedures, triggers and tables and needs further
rights for that (see
Provisioning the sync interface).
Configure Connection¶
- Navigate to Administration > Interfaces > MET/TEAM, Connection tab
- Enter the connection details of the MET/TEAM database. calServer talks to the Microsoft SQL Server of MET/TEAM directly, not to a programming interface:
| Field | Description |
|---|---|
| Product | MET/TEAM or MET/CONNECT |
| Server name | Address of the SQL Server |
| Port | Port of the SQL Server |
| Database | Name of the MET/TEAM database |
| Username | SQL Server login |
| Password | Password of this login |
- Click Test Connection or Test Login
- Save the configuration
The product version is detected, not typed
The MET/TEAM version field is read-only. calServer reads the version
from Fluke's own Version table — on every login probe and every
provisioning run. The field states when it was last read.
Until August 2026 it was an input field, and that went wrong: one customer
system had 3.4.0 typed in while the database reported 3.4.1. That is not a
display glitch — calServer V1 branches on this value
(version_compare against 3.0.0 and 3.4.0), so a stale entry silently
switches different behaviour there.
An empty field means calServer has never successfully read the counterpart — one Test login is enough.
Two probes, two questions
Test Connection asks whether the server answers, and reports its version. Test Login is the probe for real contact with MET/TEAM and works in three steps:
| Read | Answers |
|---|---|
SUSER_SNAME(), USER_NAME() |
Who does the server run the session as? |
DB_NAME() |
Which database did it end up in? |
Fluke's Version table |
Is that really MET/TEAM, and may this login read its tables? |
The third step is the point. The server answers the first two for any database at all; only the product version read back proves a MET/CAL database sits behind the login. If it stays unreadable the probe still reports success (the login did work), but says explicitly that contact is unproven rather than claiming it.
When it fails, it says why in plain words: rejected credentials, database not granted to the login, unreachable server, timeout, certificate, or a missing driver.
Neither probe saves anything. With an empty password field both test the stored password, and each answer states which of the two was tested. That distinction is what matters when changing a password: a green connection test can also mean the new password was never involved.
Provisioning the sync interface¶
Connection settings alone synchronize nothing. calServer additionally creates
its own interface inside the MET/TEAM database: stored procedures, six
triggers and the three queue tables Sync, AssetSync and AuditLog2. Fluke
ships none of that; it is calServer's part. This step is called
provisioning.
Three prerequisites, or provisioning is refused:
| Prerequisite | Where |
|---|---|
| Server name, database and username filled in | Connection tab |
| At least one sync direction enabled and saved | Synchronization tab |
Login may run DDL: CREATE PROCEDURE, CREATE TABLE, ALTER on Assets and CallSheets — or db_owner |
in SQL Server |
A toggled switch does not count
Provisioning reads the saved state, not the form. Enabling a direction and provisioning right away, without saving first, yields the refusal "no sync direction enabled". The "Objects expected" figure in the diagnostics says the same: with no direction active it lists only 13 objects, the bare scaffolding.
Paths to provisioning, all equivalent:
- Tools → Diagnostics: when it finds missing objects, "Provision now" sits next to the finding.
- Synchronization tab: when the interface stamp is out of date, the button is in the warning at the top.
- On the console:
docker exec calserver-api-v2 php artisan metteam:provision
# Afterwards re-queue the opted-in devices, or drop all local
# MET/TEAM links:
docker exec calserver-api-v2 php artisan metteam:provision --action=keep
docker exec calserver-api-v2 php artisan metteam:provision --action=reset
The queue inside MET/TEAM is reset
Provisioning recreates AssetSync and AuditLog2 (DROP and CREATE, V1
parity). Pending changes from MET/TEAM that have not been imported yet are
lost. On a first installation that is harmless; on a running system, let
the import drain first.
Why nobody noticed this in V1
In the Yii admin panel provisioning was a side effect of saving:
actionSaveConfiguration invoked it as soon as server, port, username and
password were filled in. There never was a dedicated button. V2 separates
the two deliberately — saving saves, provisioning provisions — which is
why the step is easy to miss when migrating.
Field Mapping¶
The field mapping defines how data fields are mapped between the systems:
| calServer Field | MET/TEAM Field | Direction |
|---|---|---|
| Device Name | Asset Description | Bidirectional |
| Serial Number | Serial Number | Bidirectional |
| Inventory Number | Asset ID | Bidirectional |
| Calibration Date | Cal Due Date | Import |
| Calibration Result | Cal Result | Export |
| Location | Location | Bidirectional |
| Labor time (h) | Sum of the labor entries of the work order (tab “Labor/Files”) | Import |
Labor time
Only the total of all labor entries of a work order is imported, not who booked how long. Entries booked or deleted later are picked up by the next sync. The field requires provisioning at interface version 12 or later.
Custom fields
Custom fields can also be mapped, provided they exist in both systems.
Data Import¶
There are three ways, and they cover different cases:
| Way | What for | Trigger |
|---|---|---|
| Initial migration | Take over the whole MET/TEAM stock once, for example when moving from MET/TEAM to calServer | metteam:migrate on the command line |
| Ongoing sync | Changes to devices with the sync checkbox | Scheduler, every 15 seconds |
| CallSheets of one device | Fetch individual calibrations of a device | Device view, More > Import MET/TEAM CallSheets |
Initial migration¶
metteam:migrate reads the stock of a MET/TEAM database and takes it over with
the same rules as the ongoing sync: the same field mapping, the same status
takeover, the same time conversion, the same links. A later sync therefore
recognises migrated records instead of creating them twice.
It only reads. The migration creates no procedures, triggers or tables in MET/TEAM; provisioning is not needed for it. The connection comes from the Connection tab of the selected instance.
# Count what would be taken over (reads no rows)
docker exec calserver-api-v2 php artisan metteam:migrate --list
# Dry run: read everything, write nothing
docker exec calserver-api-v2 php artisan metteam:migrate --dry-run
# Take over (asks first, --force skips the question)
docker exec calserver-api-v2 php artisan metteam:migrate
# Only some areas, a specific instance (name or uID)
docker exec calserver-api-v2 php artisan metteam:migrate --module=inventory --module=calibration
docker exec calserver-api-v2 php artisan metteam:migrate --instance='MET/TEAM Plant 2'
# Resume an aborted area after the UID it reported
docker exec calserver-api-v2 php artisan metteam:migrate --module=result --after=<UID>
The areas run in a fixed order, because each builds on the previous one:
| Area | --module |
Taken over |
|---|---|---|
| Devices and customers | inventory |
all devices not deleted, inactive ones included; customers with their devices |
| Calibrations | calibration |
all CallSheets not deleted, of devices not deleted |
| Standards | standard |
the standard assignments of these calibrations |
| Locations | location |
the current assignment per device, as in the sync |
| Measured values | result |
all measuring points of these calibrations |
Not taken over are records deleted in MET/TEAM, customers without devices and returned location assignments (the history). The status filter of the sync (IN condition) does not apply to the migration; it fetches all calibrations.
The migration reads measured values CallSheet by CallSheet, in the order of the sync: result run by creation time, within it the measuring points by their number. When a CallSheet has several result runs (say a second one after an adjustment), calServer holds the points of the first run on their numbers and those of further runs after them, just as the sync creates them. The UID an aborted measured-value area resumes after is therefore a CallSheet's.
During the migration
- Switch off the ongoing sync of this instance for the duration. Otherwise sync and migration can create the same device at the same time. The command warns when the sync is active.
- Make the first real run against a copy of the MET/TEAM database and check
the result.
metteam:restoreloads a backup into the bundled MET/TEAM runtime for this. - The migration writes row by row. With many measured values it takes hours. It can be repeated at any time; a second run updates instead of duplicating.
Rows an importer rejects are listed on the Log tab of the MET/TEAM page
with the action migrate_… and the MET/TEAM record. "Skipped" means the link is missing in
calServer, for example a standard whose calibration was not taken over. A
second run picks up what has been linked in the meantime.
Ongoing sync¶
metteam:pull fetches the changes of devices with the sync checkbox from
MET/TEAM every 15 seconds. It needs the provisioned sync interface; the
scheduler under Administration > Operations runs it. Details are under
Synchronization.
CallSheets of one device¶
In the device view, More > Import MET/TEAM CallSheets fetches individual
calibrations of the device from MET/TEAM. Taking them over requires the
calibration_edit permission.
Data Export¶
metteam:push empties the export queue to MET/TEAM every 15 seconds, provided
the outgoing push worker is enabled in the Synchronization tab. The queue
receives changes to devices with the sync checkbox and, when a device is
enabled, its calibration history as well.
Synchronization¶
Conflict avoidance
With bidirectional synchronization, conflicts can occur. Define a primary system for each data area.
Automatic synchronization can be set up as a cron job via Scheduled Tasks.
Return (closing a CallSheet)¶
In MET/TEAM a work order (CallSheet) is returned: it is closed, stamped with a return date and a returning user, and locked for editing from then on. Only from that point does MET/TEAM hold a reliable recalibration interval for its runtime to work with.
calServer can set this state, see it, and take it back.
Setting up the return status¶
The return is driven by a calibration status. In Administration -> Status management exactly one calibration status carries the METTEAM Rueckgabe (MET/TEAM return) marker.
calServer ships that status deactivated. Enabling it is a deliberate decision: from then on, every change to that status closes the linked CallSheet in MET/TEAM.
You may mark a status of your own instead. Only one may be active at a time; a second one is refused. The reason is not tidiness: on import it must be unambiguous which status a calibration closed in MET/TEAM receives.
The returning user¶
MET/TEAM records who performed a return. It resolves that name against its own user list, not against SQL Server logins.
By default the sync login acts as the returning user. That only works when this login is also kept as an application user in MET/TEAM. In many installations it is a plain database login, and then the returning user stays empty over there: the return runs, but it is attributed to nobody.
Hence the Returning user field under Settings:
- Empty means "use the sync login" — the previous behavior.
- Filled in means this name signs the return. It must match a MET/TEAM user name, not a SQL login. The connection itself is unaffected.
Whether the name lands is reported by Diagnostics: if it matches no MET/TEAM user, a warning appears there. It only shows when a return status is active — if you do not use the return, you do not need the user.
What happens when you set it¶
- calServer checks that everything the return needs is present.
- The status change is saved and the return is queued.
- The next synchronization pass (every 15 seconds) closes and returns the CallSheet in MET/TEAM.
- The calibration then becomes read-only in calServer too. Only the status remains changeable.
The lock is not an end in itself: the CallSheet is locked in MET/TEAM, any further change in calServer would never arrive there, and the two systems would drift apart.
What happens when you take it back¶
Pick a different status on the calibration. calServer reopens the CallSheet
in MET/TEAM (spl_CallSheets_REOPEN), the lock is lifted, and queued changes
are delivered.
Changes made during the return (from the import, for instance) are not lost: they wait in the queue and go through once the CallSheet is reopened.
If somebody reopens the CallSheet in MET/TEAM itself, the next import pass notices and lifts the lock.
When values are missing¶
calServer tells two cases apart.
The status change is refused when the return cannot technically happen:
| Missing | Why |
|---|---|
| Certificate number | MET/TEAM finds the CallSheet by it (cCallSheetNumber) |
| Linked device | Without a device there is no CallSheet |
| MET/TEAM sync for the device | Enable it on the device page under MET/TEAM |
| Device or calibration not transferred yet | The next synchronization pass creates them |
You get a warning when it does happen but stays incomplete:
| Missing | Consequence |
|---|---|
| Next calibration date | The return runs, but MET/TEAM keeps its own recalibration interval (tMaintNextDate) instead of taking the one from calServer |
The difference is deliberate: some installations keep the recalibration interval in MET/TEAM. Blocking them would be pure obstruction. The warning still shows up in all four places listed below, so the case does not go unnoticed.
Without a configured MET/TEAM instance none of these checks apply: the status is then an ordinary calServer status.
Where the message ends up¶
If a return fails only during transfer (connection, rejection by MET/TEAM), four places report it:
- On the calibration, a notice with the state and the reason, plus a Retry button.
- In the calibration list, the METTEAM Rueckgabe column (enable it via the column picker; filterable).
- In the protocol under Synchronization -> Returns -> Show in protocol
(source
push_calibration_close). - In the notification bell under System, for everyone holding
configure_METTEAM_synchronisation.
The Returns tile group on the Synchronization tab shows how many CallSheets are returned and how many are stuck.
When a return sits for a long time¶
While a CallSheet is returned, MET/TEAM accepts no further changes. calServer therefore holds them back instead of discarding them: they go through as soon as somebody picks a different status.
If that drags on across many passes (about twelve minutes), calServer reports it once through the protocol and the bell. The entry stays in the queue while it does: the message calls somebody, it does not throw anything away. If a return normally lasts days in your operation, you can ignore the message — nothing is lost.
Control dashboard (widgets)¶
Anyone allowed to see the MET/TEAM page (operation
configure_METTEAM_synchronisation) finds an Interfaces group with two
tiles in the dashboard widget catalogue. Without that permission the tiles are
not offered at all — and an existing tile then only states that the permission
is missing.
| Tile | Shows | Default size |
|---|---|---|
| MET/TEAM overview | The four export-queue counters (pending, processing, failed, done), the "unreachable" and "synchronisation switched off" warnings, and the Requeue failed button | full width, flat |
| MET/TEAM sync log | The most recent log entries with time, direction, device and error | full width, tall |
The default sizes are chosen so that the overview sits above the log when both are added one after the other: first the number that says that something is stuck, below it the lines that say what it is stuck on. Moving and resizing works like on any other tile.
The gear of each tile offers:
- MET/TEAM instance — which instance the tile shows. Without a choice the overview shows the primary instance and the log shows all instances. Two tiles pointing at different instances on the same dashboard are allowed, and the usual case in multi-instance operation.
- Direction (log only) — import, export or both.
- Rows (log only) — how many entries the tile shows (1–50).
- Title — a custom heading instead of the default.
The arrow in the tile header leads to this page, onto the tab that shows the
same thing at full width: Synchronization for the overview, Log for the
log, each on the tile's instance
(/admin/metteam?tab=log&instance=<uid>).
The tiles are deliberately read-only: apart from requeuing failed entries they change nothing. Configuration, connection tests, copy-all and clearing the log stay on this page.
Not part of a shared dashboard
A publicly shared dashboard renders both tiles as a note that they are not available there. The share snapshot carries no data for them — log rows contain error messages and device numbers, and who may see those is decided by a permission that does not exist without a login.
Tools¶
The Tools tab on the MET/TEAM page bundles the diagnostic and repair functions. Every tool operates on the instance selected above.
Diagnostics¶
One button, one report — read-only:
| Section | Contents |
|---|---|
| Connection | Reachability, SQL Server version, MET/TEAM version from its Version table |
| Interface | Expected versus actual provisioned objects against the MET/TEAM system catalog; missing procedures and triggers are named |
| MET/TEAM queue | AuditLog2 per table and operation with the oldest open entry, plus AssetSync and Sync |
| Export queue | Counters per status and the last protocol error on the calServer side, plus the state of the push worker: switch, scheduled task, last run |
The findings are listed at the top in plain words, most severe first.
What diagnostics find that the status does not
The Synchronization tab only knows the interface stamp — a note held in calServer, not a look into MET/TEAM. Deleting a trigger by hand there does not change that note: the tab keeps reporting the installed version while the synchronization is dead. Diagnostics look into the system catalog and name the missing objects.
When they find any, "Provision now" sits right next to the finding — including when the stamp claims everything is installed. The catalog beats the note.
A green stamp is not proof
For the same reason the Synchronization tab shows the stamp in blue rather than green: it states what the note says, not what is in MET/TEAM. To know whether the interface is actually complete, run the diagnostics. That is the only path that looks.
An old timestamp under "oldest" is direct proof that the import is not working through the queue.
Pending entries nobody collects
When entries sit under pending, the diagnosis also names why they
sit there. Three switches stand between the click and MET/TEAM, and each
of them stops the queue silently — nothing fails, nothing happens:
| Finding | What to do |
|---|---|
| The push worker is switched off | Synchronisation tab → enable "outgoing push worker" and save |
| The scheduled task "MET/TEAM sync (outbound)" is not running | Server & maintenance → Operations → enable the task |
| Task scheduled, but last run over 15 minutes ago (or never) | The Laravel scheduler is not running. It ships with autostart=false — see Controls |
Without that breakdown all three cases look identical: a number that does
not move. Sync ownership (sync_master) is the fourth switch and has its
own finding further up.
Point repair¶
Queue a single device or calibration for another export instead of running copy-all over every record. Pick the record type, enter the calServer record ID, queue it.
Clearing the protocol¶
In the Protocol tab, "Clear protocol" deletes every entry of the selected instance. Other instances are untouched.
Emptying MET/TEAM tables¶
Cannot be undone
This tool deletes data in the customer's database. It exists for initial setup and for test systems.
Visible to superadmins only. Either a single table or the fixed sync table set
(Assets, CallSheets, Facilities, Types, Points, PointUncertainties,
PointTolerances, PointReadings, PointStandardReadings).
Guards:
- The database name must be typed out exactly to confirm
- While the export queue still holds open entries, the server refuses — otherwise the push worker writes straight back into the emptied database
- Every emptied table is recorded in the system log with its row count
After emptying, MET/TEAM no longer knows about any pending changes. A copy-all run is required afterwards.
Interface procedures in the database manager¶
Under Administration → Database the MET/TEAM connection can be selected.
Besides queries, EXEC calls of the interface procedures calServer provisions
are available there, so you can reproduce what the import sees:
EXEC Get_List_Inventory_UPDATE @limit = 10
EXEC Get_Total_Calibration
EXEC Get_Inventory @nAssetUID = '…'
Only the Get_* procedures and spl_CallSheetResults_COUNT
are permitted. The Set_CopiedToMySQL_For_* procedures change the sync markers
and require write mode. Everything else, MET/TEAM's own write procedures in
particular, stays blocked.
Troubleshooting¶
| Problem | Solution |
|---|---|
| Nothing synchronizes, cause unclear | Run Tools → Diagnostics first |
| Connection error | Check server URL and credentials |
| Field mapping failed | Verify field types |
| Duplicates after import | Check unique key fields |
| Timeout | Reduce batch size |
| A single device does not arrive | Tools → Point repair |
Devices sit under pending and do not move |
Tools → Diagnostics — it names the switch that holds them |
| Saving fails only with a filled password field | Missing APP_KEY, see below |
Saving only works with an empty password field¶
Symptom: The connection settings save fine as long as the password field stays empty. As soon as a password is entered, the UI answers with an error — and Test Connection still reports green.
Cause: The API's application key APP_KEY is missing or does not fit.
calServer only ever stores passwords encrypted; without a usable key there is
nothing to store them with, and it never falls back to plain text. Everything
else on the page keeps working because this is the only encrypted field —
which is why it looks like a MET/TEAM problem when none exists. The green
connection test belongs to the same picture: it runs on the plain-text
password inherited from V1, which V2 only reads and never overwrites.
Confirm: Run Tools → Diagnostics. With the key missing, the finding
is at the top of the list and names APP_KEY explicitly.
Fix:
# Generate a key (prints it only, writes nothing)
docker exec calserver-api-v2 php artisan key:generate --show
Put the value into the installation's .env as LARAVEL_APP_KEY and restart
the API. The password then saves.
Never replace an existing APP_KEY
The key decrypts everything calServer has stored encrypted — license key, AI credentials, backup targets. Swapping it makes those values unreadable and they have to be entered again. A new key belongs only where none was set before.
MSSQL Connection Diagnostics¶
For connection issues after infrastructure upgrades (server migration, container rebuild, OS update), a dedicated console command is available. In V2:
Details and all options: Artisan Commands.
The V1 counterpart, for installations still running on Yii:
The command automatically checks:
| Step | Check |
|---|---|
| PHP Extensions | Is pdo_sqlsrv loaded? Which version? Available PDO drivers |
| ODBC Drivers | Installed drivers from /etc/odbcinst.ini |
| Credentials | Display stored connection details (password masked) |
| Network | TCP reachability of the server via fsockopen |
| PDO Connection | Direct connection test with detailed error message |
| TLS Variants | On auth failure: automatic test with TrustServerCertificate=1 and Encrypt=0 |
Override credentials:
# Test alternative password
php yiic testmssqlconnection run --password=OtherPassword
# Override all connection details
php yiic testmssqlconnection run --server=10.0.0.1 --port=1433 --username=user --password=pass --database=db
| Parameter | Description |
|---|---|
--server |
Override server IP or hostname |
--port |
Override port (default: 1433) |
--username |
Override username |
--password |
Override password |
--database |
Override database name |
Most common cause after infrastructure upgrade
ODBC Driver 18 enforces TLS encryption by default (unlike Driver 17).
If the TLS variant test succeeds with Encrypt=0 or TrustServerCertificate=1,
either a valid certificate must be configured on the SQL Server or
the DSN option in the sqlSrvDb() function needs to be adjusted.