astm

package module
v2.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 8, 2024 License: Apache-2.0 Imports: 12 Imported by: 0

README

go-astm

Library for handling the ASTM LIS2-A2 Procotol in go.

Install

go get github.com/blutspende/go-astm/v2

Features

  • Encoding UTF8, ASCII, Windows1250, Windows1251, Windows1252, DOS852, DOS855, DOS866, ISO8859_1
  • Timezone conversion
  • Custom delimiters automatically identified (defaults are ^&)

Reading ASTM

The following Go code decodes a ASTM provided as a string and stores all its information in the &message.

var message standardlis2a2.DefaultMessage

err := astm.Unmarshal([]byte(textdata), &message,
		astm.EncodingUTF8, astm.TimezoneEuropeBerlin)
if err != nil {
  log.Fatal(err)		
}

Reading ASTM with multiple message in one transmission

The same code, just use DefaultMultiMessage:

  var message standardlis2a2.DefaultMultiMessage

  astm.Unmarshal([]byte(textdata), &message,
		astm.EncodingUTF8, astm.TimezoneEuropeBerlin)		

  for _, message := range message.Messages {
	fmt.Printf("%+v", message)
  }
  

Writing ASTM

Converting an annotated Structure (see above) to an enocded bytestream.

The bytestream is encoded by-row, lacking the CR code at the end.

lines, err := astm.Marshal(msg, astm.EncodingASCII, astm.TimezoneEuropeBerlin, astm.ShortNotation)

// output on screen
for _, line := range lines {
		linestr := string(line)
		fmt.Println(linestr)
}

Identifying a message

Identifying the type of a message without decoding it. There are 3 Types of messages

  • MessageTypeQuery
  • MessageTypeOrdersOnly
  • MessageTypeOrdersAndResults
messageType, _ := astm.IdentifyMessage([]byte(astm), EncodingUTF8)

switch (messageType) {
	case MessageTypeUnkown :
	  ...
	case MessageTypeQuery :
	  ...
	case MessageTypeOrdersOnly :
	  ...
	case MessageTypeOrdersAndResults :
	  ...
}

How the go-astm library works

In order to encode (marshal) or decode (unmarshal) a message from or to lis, you need to annotate a struct in go to identify the record-types and within the record the field's location.

The Message does now the information of what type of message is mapped by annotation.

type Message struct {
	Header struct {
		field1 string `astm:"1"`
		field2 string `astm:"2"`
	} `astm:"H"` // identify the Record-Type
	PatientOrder[] struct {
		Patient struct {
			field1 string `astm:"1"`
			field2 string `astm:"2"`
			...
		} `astm:"P"`
		Order struct {
			...
		} `astm:"O"
	} 
}

The lis2a2-default implementation provided with this library as a starting point, it should fit most instruments. Alter it as required.

type CommentedResult struct {
	Result  Result    `astm:"R"`
	Comment []Comment `astm:"C,optional"`
}

type PORC struct {
	Patient         Patient   `astm:"P"`
	Comment         []Comment `astm:"C,optional"`
	Order           Order     `astm:"O"`
	CommentedResult []CommentedResult
}

type DefaultMessage struct {
	Header       Header       `astm:"H"`
	Manufacturer Manufacturer `astm:"M,optional"`
	OrderResults []PORC
	Terminator   Terminator `astm:"L"`
}
Message Structure and Annotation
type SimpleMessage struct  {
	Header       standardlis2a2.Header       `astm:"H"`
	Manufacturer standardlis2a2.Manufacturer `astm:"M,optional"`
	Patient      standardlis2a2.Patient      `astm:"P"`
	Order        standardlis2a2.Order        `astm:"O"`
	Result       standardlis2a2.Result       `astm:"R"`
	Terminator   standardlis2a2.Terminator   `astm:"L"`
}
Nested arrays
type MessagePORC struct {
	Header       standardlis2a2.Header       `astm:"H"`
	Manufacturer standardlis2a2.Manufacturer `astm:"M,optional"`
	OrderResults []struct {
		Patient         standardlis2a2.Patient `astm:"P"`
		Order           standardlis2a2.Order   `astm:"O"`
		CommentedResult []struct {
			Result  standardlis2a2.Result    `astm:"R"`
			Comment []standardlis2a2.Comment `astm:"C,optional"`
		}
	}
	Terminator standardlis2a2.Terminator `astm:"L"`
}
Addressing fields

Often the default is not enough. You can customize any record with annotation.

... by Field#
   ...
   Filed string `astm:"3"`  // Select 3rd field, start counting with 1
   ...

Example:

	X|field2|field3|field4|		Result: "field3"
	X|field2^1^2|field3^1^2|field4^5^6|		Result: "field3"	
	X|field2^1^2|field3_1^1_1^2_!\\field3_2^5_2^2_2|field4^6^2|		Result: "field3_1"
... by Field#.Component#
   ...
   Filed string `astm:"3.2"`  // Select 3rd field, 2nd component, start counting with 1
   ...

Example:

	X|field2|field3|field4|		Result: ""	
	X|field2^1^2|field3^1^2|field4^5^6|		Result: "1"	
	X|field2^1^2|field3_1^1_1^2_!\\field3_2^1_2^2_2|field4^1^2|		Result: "1_1"
... by Field#.Repeat#.Component#
   ...
   Filed string `astm:"3.2.2"`  // Select 3rd field, 2nd array index, 2nd component, start counting with 1
   ...

Example:

	X|field2|field3|field4|		Result: ""	
	X|field2^1^2|field3^1^2|field4^5^6|		Result: ""	
	X|field2^1^2|field3_1^1_1^2_!\\field3_2^1_2^2_2|field4^1^2|		Result: "1_2"
Custom Record Format
type Result struct {
	SequenceNumber                           int       `astm:"2,sequence"`   // sequence generates numbers when value is 0 
	UniversalTestID                          string    `astm:"3.1"`         
	UniversalTestIDName                      string    `astm:"3.2"`         
	UniversalTestIDType                      string    `astm:"3.3"`         
	ManufacturersTestType                    string    `astm:"3.4"`         
	ManufacturersTestName                    string    `astm:"3.5"`         
	ManufacturersTestCode                    string    `astm:"3.6"`         
	TestCode                                 string    `astm:"3.7"`         
	DataMeasurementValue                     string    `astm:"4.1"`         
	InitialMeasurementValue                  string    `astm:"4.2"`         
	MeasurementValueOfDevice                 string    `astm:"4.3"`         
	Units                                    string    `astm:"5"`           
	ReferenceRange                           string    `astm:"6"`           
	ResultAbnormalFlag                       string    `astm:"7"`           
	NatureOfAbnormalTesting                  string    `astm:"8"`           
	ResultStatus                             string    `astm:"9"`           
	DateOfChangeInInstrumentNormativeTesting time.Time `astm:"10,longdate"` 
	OperatorIDPerformed                      string    `astm:"11.1"`        
	OperatorIDVerified                       string    `astm:"11.2"`        
	DateTimeTestStarted                      time.Time `astm:"12,longdate"` 
	DateTimeCompleted                        time.Time `astm:"13,longdate"` 
	IntstrumentIdentification                string    `astm:"14"`          
}

Documentation

Index

Constants

View Source
const (
	ANNOTATION_DELIMITER = "delimiter" // annotation that triggers the delimiters in the scanner to be reset
	ANNOTATION_REQUIRED  = "require"   // field-annotation: by default all fields are optinal
	ANNOTATION_OPTIONAL  = "optional"  // record-annotation: by default all records are mandatory
	ANNOTATION_SEQUENCE  = "sequence"  // indicating that a sequence number should be generated (output only)
	ANNOTATION_LONGDATE  = "longdate"
)
View Source
const MAX_DEPTH = 44
View Source
const MAX_MESSAGE_COUNT = 44
View Source
const ShortNotation = 2
View Source
const StandardNotation = 1

Variables

This section is empty.

Functions

func EncodeCharsetToUTF8From

func EncodeCharsetToUTF8From(charmap *charmap.Charmap, data []byte) ([]byte, error)

func EncodeUTF8ToCharset

func EncodeUTF8ToCharset(charmap *charmap.Charmap, data []byte) []byte

func Marshal

func Marshal(message interface{}, enc Encoding, tz Timezone, notation Notation) ([][]byte, error)

* Marshal - wrap datastructure to code *

func Unmarshal

func Unmarshal(messageData []byte, targetStruct interface{}, enc Encoding, tz Timezone) error

Types

type Encoding

type Encoding int
const EncodingASCII Encoding = 2
const EncodingDOS852 Encoding = 6
const EncodingDOS855 Encoding = 7
const EncodingDOS866 Encoding = 8
const EncodingISO8859_1 Encoding = 9
const EncodingUTF8 Encoding = 1
const EncodingWindows1250 Encoding = 3
const EncodingWindows1251 Encoding = 4
const EncodingWindows1252 Encoding = 5

type LineBreak

type LineBreak int
const CR LineBreak = 0x0D
const CRLF LineBreak = 0x0D0A
const LF LineBreak = 0x0A

type MessageType

type MessageType int
const MessageTypeOrdersAndResults MessageType = 3
const MessageTypeOrdersOnly MessageType = 2
const MessageTypeQuery MessageType = 1
const MessageTypeUnkown MessageType = -1

func IdentifyMessage

func IdentifyMessage(messageEncoded []byte, enc Encoding) (MessageType, error)

type Notation

type Notation int
Notation defines how the output format is build

ShortNotation will skip all delimiters to the right of the last value StandardNotation will always produce as many delimiters as there are values in the export-format

type OutputRecord

type OutputRecord struct {
	Field, Repeat, Component int
	Value                    string
}

type OutputRecords

type OutputRecords []OutputRecord

func (OutputRecords) Len

func (or OutputRecords) Len() int

used for sorting

func (OutputRecords) Less

func (or OutputRecords) Less(i, j int) bool

func (OutputRecords) Swap

func (or OutputRecords) Swap(i, j int)

type RETV

type RETV int
const (
	OK         RETV = 1
	UNEXPECTED RETV = 2 // an exit that wont abort processing. used for skipping optional records
	ERROR      RETV = 3 // a definite error that stops the process
)

type Timezone

type Timezone string
const TimezoneEuropeBerlin Timezone = "Europe/Berlin"
const TimezoneEuropeBudapest Timezone = "Europe/Budapest"
const TimezoneEuropeLondon Timezone = "Europe/London"
const TimezoneUTC Timezone = "UTC"

Directories

Path Synopsis
lib
standardlis2a2
Standard implementation for the lis2a2 protocol according to standard in every detail, will work for most with some tinkering....
Standard implementation for the lis2a2 protocol according to standard in every detail, will work for most with some tinkering....

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL