; Mecmesin BFG Force Gauge
; Version 1.1
; Issued  27 Aug 2023
;
; The BFG series are a "Basic Force Gauge" and this is reflected by their serial communications.
; The units can not be queried to determine their current unit of measurement or whether they are 
; indicating real-time or captured "MAX" values. 
; This device definition file determines the selected unit of measurement by checking the 
; number of decimal places in the measurement, and cycling through the different units 
; when the number of decimal places is the same for more than one unit of measurement.
;
; The "Live Data" mode will capture the real-time measurement. The "Live & Max Data" mode will 
; log the real-time, max tension, and max compression values - but this incurs a significant time 
; penalty as the device can not reliably report the correct values until 90ms after toggling the 
; "MAX" display function.
; It should be noted that the "Live Data" mode can be used to capture ONLY maximum tension or ONLY 
; maximum compression, if that is desired. Simply toggle the "MAX" display (press the button on the
; unit or use the control in the setup dialogue within TestController).
;
; THIS HAS ONLY BEEN TESTED WITH A BFG1000. It likely works perfectly with a BFG2500. 
; Queries of the unit of measurement will not work with other models until someone with access
; to one of these models can update the definition file, or provide the necessary information to 
; the author jmurray@jmaudio.com.au
;
;--------------------------------------------------------------------------
; BFG10		UNTESTED
#metadef
;#metaDebug
#idString MECMESIN,BFG10
#name Mecmesin BFG 10 Streaming
#baudrate 57600
#sections STREAMING_MODE BFG_LO BFG10_SPECIFIC UNITS_mN UNITS_N UNITS_g UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D3
#replaceText g.dec D1
#replaceText kg.dec D4
#replaceText oz.dec D2
#replaceText lb.dec D4
#remove #cmdMode Unit:_kN

#metadef
;#metaDebug
#idString MECMESIN,BFG10
#name Mecmesin BFG 10 Command
#baudrate 9600
#sections COMMAND_MODE BFG_LO BFG10_SPECIFIC UNITS_mN UNITS_N UNITS_g UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D3
#replaceText g.dec D1
#replaceText kg.dec D4
#replaceText oz.dec D2
#replaceText lb.dec D4
#remove #cmdMode Unit:_kN

;--------------------------------------------------------------------------
; BFG50		UNTESTED
#metadef
;#metaDebug
#idString MECMESIN,BFG50
#name Mecmesin BFG 50 Streaming
#baudrate 57600
#sections STREAMING_MODE BFG_LO BFG50_SPECIFIC UNITS_mN UNITS_N UNITS_g UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D2
#replaceText g.dec D0
#replaceText kg.dec D3
#replaceText oz.dec D2
#replaceText lb.dec D3
#remove #cmdMode Unit:_kN

#metadef
;#metaDebug
#idString MECMESIN,BFG50
#name Mecmesin BFG 50 Command
#baudrate 9600
#sections COMMAND_MODE BFG_LO BFG50_SPECIFIC UNITS_mN UNITS_N UNITS_g UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D2
#replaceText g.dec D0
#replaceText kg.dec D3
#replaceText oz.dec D2
#replaceText lb.dec D3
#remove #cmdMode Unit:_kN

;--------------------------------------------------------------------------
; BFG200	UNTESTED
#metadef
;#metaDebug
#idString MECMESIN,BFG200
#name Mecmesin BFG 200 Streaming
#baudrate 57600
#sections STREAMING_MODE BFG_MID BFG200_SPECIFIC UNITS_N UNITS_g UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D2
#replaceText g.dec D0
#replaceText kg.dec D3
#replaceText oz.dec D1
#replaceText lb.dec D2
#remove #cmdMode Unit:_mN
#remove #cmdMode Unit:_kN

#metadef
;#metaDebug
#idString MECMESIN,BFG200
#name Mecmesin BFG 200 Command
#baudrate 9600
#sections COMMAND_MODE BFG_MID BFG200_SPECIFIC UNITS_N UNITS_g UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D2
#replaceText g.dec D0
#replaceText kg.dec D2
#replaceText oz.dec D1
#replaceText lb.dec D2
#remove #cmdMode Unit:_mN
#remove #cmdMode Unit:_kN

;--------------------------------------------------------------------------
; BFG500	UNTESTED
#metadef
;#metaDebug
#idString MECMESIN,BFG500
#name Mecmesin BFG 500 Streaming
#baudrate 57600
#sections STREAMING_MODE BFG_MID BFG500_SPECIFIC UNITS_N UNITS_g UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D1
#replaceText g.dec D0
#replaceText kg.dec D2
#replaceText oz.dec D1
#replaceText lb.dec D2
#remove #cmdMode Unit:_mN
#remove #cmdMode Unit:_kN

#metadef
;#metaDebug
#idString MECMESIN,BFG500
#name Mecmesin BFG 500 Command
#baudrate 9600
#sections COMMAND_MODE BFG_MID BFG500_SPECIFIC UNITS_N UNITS_g UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D1
#replaceText g.dec D0
#replaceText kg.dec D2
#replaceText oz.dec D1
#replaceText lb.dec D2
#remove #cmdMode Unit:_mN
#remove #cmdMode Unit:_kN

;--------------------------------------------------------------------------
; BFG1000
#metadef
;#metaDebug
#idString MECMESIN,BFG1000
#name Mecmesin BFG 1000 Streaming
#baudrate 57600
#sections STREAMING_MODE BFG_HI BFG1000_SPECIFIC UNITS_N UNITS_kN UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D1
#replaceText kg.dec D2
#replaceText oz.dec D0
#replaceText lb.dec D2
#remove #cmdMode Unit:_mN
#remove #cmdMode Unit:_gf

#metadef
;#metaDebug
#idString MECMESIN,BFG1000
#name Mecmesin BFG 1000 Command
#baudrate 9600
#sections COMMAND_MODE BFG_HI BFG1000_SPECIFIC UNITS_N UNITS_kN UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D1
#replaceText kg.dec D2
#replaceText oz.dec D0
#replaceText lb.dec D2
#remove #cmdMode Unit:_mN
#remove #cmdMode Unit:_gf

;--------------------------------------------------------------------------
; BFG2500	UNTESTED
#metadef
;#metaDebug
#idString MECMESIN,BFG2500
#name Mecmesin BFG 2500 Streaming
#baudrate 57600
#sections STREAMING_MODE BFG_HI BFG2500_SPECIFIC UNITS_N UNITS_kN UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D1
#replaceText kg.dec D2
#replaceText oz.dec D0
#replaceText lb.dec D1
#remove #cmdMode Unit:_mN
#remove #cmdMode Unit:_gf

#metadef
;#metaDebug
#idString MECMESIN,BFG2500
#name Mecmesin BFG 2500 Command
#baudrate 9600
#sections COMMAND_MODE BFG_HI BFG2500_SPECIFIC UNITS_N UNITS_kN UNITS_kg UNITS_oz UNITS_lb
#replaceText N.dec D1
#replaceText kg.dec D2
#replaceText oz.dec D0
#replaceText lb.dec D1
#remove #cmdMode Unit:_mN
#remove #cmdMode Unit:_gf

;--------------------------------------------------------------------------
; Basic Definition
#meta
#author jmurray@jmaudio.com.au
#idstring MECMESIN,BFG,
#name MECMESIN BFG
#handle BFG
#port comfixedbaud
#baudrate 9600
#driver AsciiBlock
#notes Device must be streaming data before a "Reconnect" is performed to the Streaming Mode definition, else TestController will time out.
Please note that the continuous data stream only starts when approximately 2% of the rated capacity of the gauge is reached.

;--------------------------------------------------------------------------
; A list of possible column name with unit and formatter (SI, Time, Int, D0..D6)

#metaSection UNITS_mN
#value Force.mN mN D0 live.mN,max.mN
#value MaxTension.mN mN D0 max.mN
#value MaxCompression.mN mN D0 max.mN

#metaSection UNITS_N
#value Force.N N N.dec live.N,max.N
#value MaxTension.N N N.dec max.N
#value MaxCompression.N N N.dec max.N

#metaSection UNITS_kN
#value Force.kN kN D4 live.kN,max.kN
#value MaxTension.kN kN D4 max.kN
#value MaxCompression.kN kN D4 max.kN

#metaSection UNITS_g
#value Force.g gf g.dec live.g,max.g
#value MaxTension.g gf g.dec max.g
#value MaxCompression.g gf g.dec max.g

#metaSection UNITS_kg
#value Force.kg kgf kg.dec live.kg,max.kg
#value MaxTension.kg kgf kg.dec max.kg
#value MaxCompression.kg kgf kg.dec max.kg

#metaSection UNITS_oz
#value Force.oz oz oz.dec live.oz,max.oz
#value MaxTension.oz oz oz.dec max.oz
#value MaxCompression.oz oz oz.dec max.oz

#metaSection UNITS_lb
#value Force.lb lbf lb.dec live.lb,max.lb
#value MaxTension.lb lbf lb.dec max.lb
#value MaxCompression.lb lbf lb.dec max.lb

;--------------------------------------------------------------------------
#metaSection BFG_LO	;UNTESTED - CHECK unitTags
; Section common to both "lower force" models of BFG: BFG10 and BFG50
; array of units should be in order that the device rotates through
#scpiCmd init:model none
:setvar: unitTags = "mN N kg g oz lb"
:setvar: number_of_units = 6

;--------------------------------------------------------------------------
#metaSection BFG10_SPECIFIC		;UNTESTED
; This will not work until someone with a BFG10 can make the necessary changes or provide the required
; information to jmurray@jmaudio.com.au
#scpiCmd UNITS? #pgm#
var value = deviceRead(handle,"?");
; if decimal place not found
if indexOf(value,".") < 0
	;unit is mN
	unitsIndex = 0;
;else if returned value has 2 decimal places
else
	if strlen(substring(value,indexOf(value,".")+1)) == 4
		; toggle unit once
		sleep(10)
		deviceWrite(handle,"BTN:UNITS");
		sleep(10)
		value = "";
			value = deviceRead(handle,"?");
		; check decimal places for 1 (g) or 0 (mN)
		if strlen(substring(value,indexOf(value,".")+1)) == 1
			unitsIndex = 2;
		elseif indexOf(value,".") < 0
			unitsIndex = 5;
		endif;
		;toggle unit 4 times 
		var i = 0;
		for i = 0 to (number_of_units - 2) do
			sleep(10)
			deviceWrite(handle,"BTN:UNITS");
		endfor;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 1
		unitsIndex = 3;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 2
		unitsIndex = 4;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 3
		unitsIndex = 1;
	endif;
endif;
units = getElement(unitTags,unitsIndex);
mode = readMode + "." + units;
print(units)

;--------------------------------------------------------------------------
#metaSection BFG50_SPECIFIC		;UNTESTED
; This will not work until someone with a BFG50 can make the necessary changes or provide the required
; information to jmurray@jmaudio.com.au
#scpiCmd UNITS? #pgm#
var value = deviceRead(handle,"?");
; if decimal place not found
if indexOf(value,".") < 0
	;unit is g
	unitsIndex = 2;
;else if returned value has 2 decimal places
else
	if strlen(substring(value,indexOf(value,".")+1)) == 2
		; toggle unit once
		sleep(10)
		deviceWrite(handle,"BTN:UNITS");
		sleep(10)
		value = "";
			value = deviceRead(handle,"?");
		; check decimal places for 3 (kg) or 2 (lb)
		if strlen(substring(value,indexOf(value,".")+1)) == 3
			unitsIndex = 0;
		elseif strlen(substring(value,indexOf(value,".")+1)) == 2
			unitsIndex = 4;
		endif;
		;toggle unit 4 times 
		var i = 0;
		for i = 0 to (number_of_units - 2) do
			sleep(10)
			deviceWrite(handle,"BTN:UNITS");
		endfor;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 1
		unitsIndex = 3;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 3
		unitsIndex = 1;
	endif;
endif;
units = getElement(unitTags,unitsIndex);
mode = readMode + "." + units;
print(units)

;--------------------------------------------------------------------------
#metaSection BFG_MID	;UNTESTED - CHECK unitTags
; Section common to both "medium force" models of BFG: BFG200 and BFG500
; array of units should be in order that the device rotates through
#scpiCmd init:model none
:setvar: unitTags = "N kg g oz lb"
:setvar: number_of_units = 5

;--------------------------------------------------------------------------
#metaSection BFG200_SPECIFIC	;UNTESTED
; This will not work until someone with a BFG200 can make the necessary changes or provide the required
; information to jmurray@jmaudio.com.au
#scpiCmd UNITS? #pgm#
var value = deviceRead(handle,"?");
; if decimal place not found
if indexOf(value,".") < 0
	;unit is g
	unitsIndex = 2;
;else if returned value has 2 decimal places
else
	if strlen(substring(value,indexOf(value,".")+1)) == 2
		; toggle unit once
		sleep(10)
		deviceWrite(handle,"BTN:UNITS");
		sleep(10)
		value = "";
			value = deviceRead(handle,"?");
		; check decimal places for 3 (kg) or 2 (lb)
		if strlen(substring(value,indexOf(value,".")+1)) == 3
			unitsIndex = 0;
		elseif strlen(substring(value,indexOf(value,".")+1)) == 2
			unitsIndex = 4;
		endif;
		;toggle unit 4 times 
		var i = 0;
		for i = 0 to (number_of_units - 2) do
			sleep(10)
			deviceWrite(handle,"BTN:UNITS");
		endfor;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 1
		unitsIndex = 3;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 3
		unitsIndex = 1;
	endif;
endif;
units = getElement(unitTags,unitsIndex);
mode = readMode + "." + units;
print(units)

;--------------------------------------------------------------------------
#metaSection BFG500_SPECIFIC	;UNTESTED
; This will not work until someone with a BFG500 can make the necessary changes or provide the required
; information to jmurray@jmaudio.com.au
#scpiCmd UNITS? #pgm#
var value = deviceRead(handle,"?");
; if decimal place not found
if indexOf(value,".") < 0
	;unit is g
	unitsIndex = 2;
;else if returned value has 2 decimal places
else
	if strlen(substring(value,indexOf(value,".")+1)) == 2
		; toggle unit once
		sleep(10)
		deviceWrite(handle,"BTN:UNITS");
		sleep(10)
		value = "";
			value = deviceRead(handle,"?");
		; check decimal places for ?
		if strlen(substring(value,indexOf(value,".")+1)) == 4
			unitsIndex = 1;
		elseif strlen(substring(value,indexOf(value,".")+1)) == 1
			unitsIndex = 4;
		endif;
		;toggle unit 4 times 
		var i = 0;
		for i = 0 to (number_of_units - 2) do
			sleep(10)
			deviceWrite(handle,"BTN:UNITS");
		endfor;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 1
		unitsIndex = 0;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 4
		unitsIndex = 2;
	endif;
endif;
units = getElement(unitTags,unitsIndex);
mode = readMode + "." + units;
print(units)

;--------------------------------------------------------------------------
#metaSection BFG_HI
; Section common to both "higher force" models of BFG: BFG1000 and BFG2500
; array of units should be in order that the device rotates through
#scpiCmd init:model none
:setvar: unitTags = "N kg kN oz lb"
:setvar: number_of_units = 5

;--------------------------------------------------------------------------
#metaSection BFG1000_SPECIFIC
; This routine specifically for determining units of measurement based on decimal places returned by readings
; from a BFG1000. 
#scpiCmd UNITS? #pgm#
var value = deviceRead(handle,"?");
; if decimal place not found
if indexOf(value,".") < 0
	;unit is oz
	unitsIndex = 3;
;else if returned value has 2 decimal places
else
	if strlen(substring(value,indexOf(value,".")+1)) == 2
		; toggle unit once
		sleep(10)
		deviceWrite(handle,"BTN:UNITS");
		sleep(10)
		value = "";
			value = deviceRead(handle,"?");
		; check decimal places for 4 (kN) or 1 (N)
		if strlen(substring(value,indexOf(value,".")+1)) == 4
			unitsIndex = 1;
		elseif strlen(substring(value,indexOf(value,".")+1)) == 1
			unitsIndex = 4;
		endif;
		;toggle unit 4 times 
		var i = 0;
		for i = 0 to (number_of_units - 2) do
			sleep(10)
			deviceWrite(handle,"BTN:UNITS");
		endfor;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 1
		unitsIndex = 0;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 4
		unitsIndex = 2;
	endif;
endif;
units = getElement(unitTags,unitsIndex);
mode = readMode + "." + units;
print(units)

;--------------------------------------------------------------------------
#metaSection BFG2500_SPECIFIC	;UNTESTED
; This routine specifically for determining units of measurement based on decimal places returned by readings
; from a BFG2500. 
; Untested but should be very similar to BFG1000.
#scpiCmd UNITS? #pgm#
var value = deviceRead(handle,"?");
; if decimal place not found
if indexOf(value,".") < 0
	;unit is oz
	unitsIndex = 3;
;else if returned value has 2 decimal places
else
	if strlen(substring(value,indexOf(value,".")+1)) == 1
		; toggle unit once
		sleep(10)
		deviceWrite(handle,"BTN:UNITS");
		sleep(10)
		value = "";
			value = deviceRead(handle,"?");
		; check decimal places for 4 (kN) or 1 (N)
		if strlen(substring(value,indexOf(value,".")+1)) == 2
			unitsIndex = 0;
		elseif strlen(substring(value,indexOf(value,".")+1)) == 1
			unitsIndex = 4;
		endif;
		;toggle unit 4 times 
		var i = 0;
		for i = 0 to (number_of_units - 2) do
			sleep(10)
			deviceWrite(handle,"BTN:UNITS");
		endfor;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 2
		unitsIndex = 1;
	elseif strlen(substring(value,indexOf(value,".")+1)) == 4
		unitsIndex = 2;
	endif;
endif;
units = getElement(unitTags,unitsIndex);
mode = readMode + "." + units;
print(units)

;--------------------------------------------------------------------------
#metaSection COMMAND_MODE

#eol \_
#rxEol \r\n

;--------------------------------------------------------------------------
; Functions as scpiCmds
#scpiCmd READ? #pgm#
if substring(mode,0,indexOf(mode,".")) == "live"
	value = replace(deviceRead(handle,"?")," ","")
	print(value)
elseif substring(mode,0,indexOf(mode,".")) == "max"
	var resultArray[3];
	var i = 0;
	for i = 0 to 2 do
		resultArray[i] = replace(deviceRead(handle,"?")," ","");
		sleep(10);
		deviceWrite(handle,"MAX");
		sleep(90);
	endfor;
	print(string(resultArray[0]) + " " + string(resultArray[1]) + " " + string(resultArray[2]))
endif

#scpiCmd readMode #pgm# (value)
if value == "live"
	readMode = "live"
elseif value == "max"
	readMode = "max"
else
	print("INVALID!")
endif
mode = readMode + "." + units;

#scpiCmd loc:units #pgm#
deviceWrite(handle,"BTN:UNITS")
unitsIndex = unitsIndex + 1
if unitsIndex > (number_of_units - 1)
	unitsIndex = 0
endif;

#askValues READ?
#askValuesReadFormat fff

;--------------------------------------------------------------------------
; Setup Popup

#cmdSetup infoAsk TXD
:layout:
:read: TXD?
:readmath: replace(value," ","")

;--------------------------------------------------------------------------
; Mode Popup

#cmdMode Live_Data live
readMode live

#cmdMode Live_&_Max_Data max
readMode max

;--------------------------------------------------------------------------
#metaSection STREAMING_MODE

#rxStart 
#rxEnd \r\n
#rxCount 1
;#rxFormat fffffff
#rxFormat .*?([\s0-9.E+-]+).*?([\s0-9.E+-]+).*?([\s0-9.E+-]+).*?([\s0-9.E+-]+).*?([\s0-9.E+-]+).*?([\s0-9.E+-]+).*?([\s0-9.E+-]+)

#askValues values?
#askValuesMathFormat replace(value," ","")
#askValuesReadFormat f
;#askValuesReadFormat .*?([0-9.E+-]+).*?([0-9.E+-]+).*?([0-9.E+-]+).*?([0-9.E+-]+).*?([0-9.E+-]+).*?([0-9.E+-]+).*?([0-9.E+-]+)

#cmdSetup button End_Streaming_Data
:write: TXD?
:tip: WARNING: Streaming can only resume when physical TXD button held on device for > 2s. If streaming ended, device can be connected to in "Command Mode"

;--------------------------------------------------------------------------
#metaSection

;--------------------------------------------------------------------------
; The following are common to both Command (query/response) mode and Streaming mode

#eol \_

#scpiCmd ? txrx1? ?
#scpiCmd TXD? txrx1? \x01
#scpiCmd BTN:UNITS tx \x02
#scpiCmd MAX tx \x03
#scpiCmd RESET tx \x04
#scpiCmd ZERO tx \x05
#scpiCmd OFF tx \x0F

#initCmd init;init:model

#scpiCmd init none
:setvar: units="unknown";
:setvar: unitsIndex = 0
:setvar: readMode = "live";
:setvar: mode = "unknown";

#scpiCmd MODE? #pgm#
deviceRead(handle,"UNITS?")
print(mode)

#scpiCmd loc:units? #pgm#
print(getElement(unitTags,unitsIndex))

#scpiCmd set:units #pgm# (value)
var target_index = listIndex(value,unitTags)
var current_index = listIndex(units,unitTags)
if target_index != current_index
	if target_index > current_index
		var number_of_toggles = target_index - current_index
	elseif target_index < current_index
		var number_of_toggles = (number_of_units - current_index) + target_index
	endif
	var i = 1
	for i = 1 to number_of_toggles do
		deviceWrite(handle,"BTN:UNITS")
		sleep(10)
	endfor
endif
unitsIndex = target_index
units = getElement(unitTags,unitsIndex)

;--------------------------------------------------------------------------
; Mode Specfications

#askMode MODE?
#mayModifyMode MODE? UNITS? readMode

;--------------------------------------------------------------------------
; Mode Popup

#cmdMode Unit:_mN mN
set:units mN

#cmdMode Unit:_N N
set:units N

#cmdMode Unit:_kN kN
set:units kN

#cmdMode Unit:_gf g
set:units g

#cmdMode Unit:_kgf kg
set:units kg

#cmdMode Unit:_ozf oz
set:units oz

#cmdMode Unit:_lbf lb
set:units lb

;--------------------------------------------------------------------------
; Setup Popup

#cmdSetup info UoM
:read: loc:units?
:updatemodechange:

#cmdSetup button MAX
:write: MAX
:tip: Toggle between display of current measurement, maximum tension, or maximum compression.

#cmdSetup button RESET
:write: RESET
:tip: Resets the peak tension and compression values.

#cmdSetup button ZERO
:write: ZERO
:tip: Tare the instrument to cancel out attachments or gravity (change of orienation

#cmdSetup button OFF
:write: OFF
:tip: Turn off the instrument. It can only be turned on again by pressing the power button on the instrument.