secs4java8

repository·master·Indexed 41 days ago

https://github.com/kenta-shimizu/secs4java8

A Java 8 implementation of SEMI-SECS communication protocols. It supports SECS-I (SEMI-E4), SECS-II (SEMI-E5), GEM (SEMI-E30, partially), HSMS (SEMI-E37), HSMS-SS (SEMI-E37.1), HSMS-GS (SEMI-E37.2), and SML (SEMI Message Language).

Tokens
6.1K
Snippets
14
Records
16
Agent score
51%

What's inside secs4java8

  1. Supported SECS protocols and standards

    master

    secs4java8 is a SEMI-SECS-communicate implementation for Java 8 that supports the following standards:

    • SECS-I (SEMI-E4)
    • SECS-II (SEMI-E5)
    • GEM (SEMI-E30, partially)
    • HSMS (SEMI-E37), HSMS-SS (SEMI-E37.1), and HSMS-GS (SEMI-E37.2)
    • SML (SEMI Message Language)
  2. Send a Primary-Message and receive a Reply-Message

    master

    To send a message, first construct a Secs2 object representing the SECS-II data. Then use the SecsCommunicator#send method.

    Important Notes:

    • SecsCommunicator#send is a blocking method. It will wait until the reply is received or a timeout occurs.
    • If the W-Bit is true, the method returns an Optional<SecsMessage> containing the reply. If W-Bit is false, the Optional is empty.
    • If a T3-Timeout occurs, a SecsWaitReplyMessageException is thrown.
    /* 1. Create SECS-II */
    Secs2 secs2 = Secs2.list(
        Secs2.binary((byte)0x81),           /* <B  0x81>              */
        Secs2.uint2(1001),                  /* <U2 1001>              */
        Secs2.ascii("ON FIRE")              /* <A  "ON FIRE">         */
    );
    
    /* 2. Send Primary-Message and 3. Receive Reply-Message */
    Optional<SecsMessage> reply = passive.send(
        5,          /* Stream-Number   */
        1,          /* Function-Number */
        true,       /* W-Bit           */
        secs2       /* SECS-II         */
    );
  3. Configure Dynamic Event Reporting using GEM

    master

    Dynamic Event Reporting (GEM) allows you to configure how the system handles event collection and report definitions. You can use aliases for reports and collection events to simplify interaction via S6F19/S6F21 (for reports) and S6F15/S6F17 (for events).

    Workflow:

    1. Create Configuration: Initialize a DynamicEventReportConfig via SecsCommunicator#gem().newDynamicEventReportConfig().
    2. Define Reports: Use addDefineReport to specify report contents. You can provide an optional alias.
    3. Enable Collection Events: Use addEnableCollectionEvent to specify CEIDs. You can provide an optional alias.
    4. Link Events to Reports: Use addLinkByReport (using the DynamicReport object) or addLinkById (using the RPTID) to associate a collection event with one or more reports.
    5. Execute Scenarios: Use scenario methods like s2f37DisableAll(), s2f33DeleteAll(), s2f33Define(), s2f35(), or s2f37Enable() to apply the configuration.
    // 1. Create Configuration
    DynamicEventReportConfig evRptConf = active.gem().newDynamicEventReportConfig();
    
    // 2. Add Reports (with and without aliases)
    DynamicReport rptSimple = evRptConf.addDefineReport(Arrays.asList(Long.valueOf(1001L)));
    DynamicReport rptAlias = evRptConf.addDefineReport("report-alias", Arrays.asList(Long.valueOf(2001L)));
    
    // 3. Add Collection Events (with and without aliases)
    DynamicCollectionEvent evSimple = evRptConf.addEnableCollectionEvent(101L);
    DynamicCollectionEvent evAlias = evRptConf.addEnableCollectionEvent("event-alias", 201L);
    
    // 4. Add Links
    evRptConf.addLinkByReport(evAlias, Arrays.asList(rptAlias));
    evRptConf.addLinkById(evAlias, Arrays.asList(Long.valueOf(1L)));
    
    // 5. Execute Scenario
    ERACK erack = evRptConf.s2f37Enable();
  4. Detect Communicatable-state changes

    master

    You can monitor the connection state of a SecsCommunicator using listeners or blocking wait methods.

    Using a Listener

    Add a listener via addSecsCommunicatableStateChangeListener. Note that this listener is a blocking method; ensure the logic inside the listener passes through quickly.

    Using Blocking Wait Methods

    Use these methods to pause execution until the state changes:

    • waitUntilCommunicatable(): Blocks until the state becomes communicatable.
    • waitUntilNotCommunicatable(): Blocks until the state becomes non-communicatable.
    • openAndWaitUntilCommunicatable(): Opens the communicator and blocks until it is communicatable.
    /* Add-Listener example */
    active.addSecsCommunicatableStateChangeListener((boolean communicatable) -> {
        if ( communicatable ) {
            System.out.println("communicatable");
        } else {
            System.out.println("not communicatable");
        }
    });
    
    /* Blocking wait examples */
    active.waitUntilCommunicatable();
    active.waitUntilNotCommunicatable();
    active.openAndWaitUntilCommunicatable();
  5. Create and open an HSMS-SS Communicator

    master

    To use HSMS-SS, you can configure the communicator in either PASSIVE or ACTIVE mode using HsmsSsCommunicatorConfig.

    HSMS-SS Passive Mode

    Use this configuration for a passive connection (e.g., a server/receiver role).

    HSMS-SS Active Mode

    Use this configuration for an active connection (e.g., a client/initiator role).

    Both modes require setting a socketAddress, sessionId, and isEquip status. You can also configure specific timeouts (T3, T5, T6, T7, T8) and GEM parameters like mdln, softrev, and clockType.

    /* HSMS-SS-Passive open example */
    HsmsSsCommunicatorConfig config = new HsmsSsCommunicatorConfig();
    config.connectionMode(HsmsConnectionMode.PASSIVE);
    config.socketAddress(new InetSocketAddress("127.0.0.1", 5000));
    config.sessionId(10);
    config.isEquip(true);
    config.timeout().t3(45.0F);
    config.timeout().t6( 5.0F);
    config.timeout().t7(10.0F);
    config.timeout().t8( 5.0F);
    config.gem().mdln("MDLN-A");
    config.gem().softrev("000001");
    config.gem().clockType(ClockType.A16);
    
    SecsCommunicator passive = HsmsSsCommunicator.newInstance(config);
    passive.open();
    
    /* HSMS-SS-Active open example */
    HsmsSsCommunicatorConfig config = new HsmsSsCommunicatorConfig();
    config.connectionMode(HsmsConnectionMode.ACTIVE);
    config.socketAddress(new InetSocketAddress("127.0.0.1", 5000));
    config.sessionId(10);
    config.isEquip(false);
    config.timeout().t3(45.0F);
    config.timeout().t5(10.0F);
    config.timeout().t6( 5.0F);
    config.timeout().t8( 5.0F);
    config.linktest(120.0F);
    config.gem().clockType(ClockType.A16);
    
    SecsCommunicator active = HsmsSsCommunicator.newInstance(config);
    active.open();
  6. Configure the serial device in run.sh

    master

    It is recommended to use a USB-Serial cable (e.g., Buffalo BSUSRC0605BS). If your serial device is not on the default port, you must edit the run.sh script to specify the correct device path using the --device flag.

    For example, if your device is at /dev/ttyUSB0, update run.sh as follows:

    # Edit run.sh to include the device path
    --device /dev/ttyUSB0
  7. Create and open an HSMS-GS-Communicator instance

    master

    To use the HSMS-GS protocol, you must configure an HsmsGsCommunicatorConfig object and pass it to HsmsGsCommunicator.newInstance(config). You can operate in either PASSIVE or ACTIVE connection modes.

    Passive Mode Configuration

    In PASSIVE mode (typically used by equipment), you define the socket address, set isEquip(true), and can specify a rebindIfPassive interval.

    Active Mode Configuration

    In ACTIVE mode (typically used by host systems), you set isEquip(false), define a connectionMode(HsmsConnectionMode.ACTIVE), and can specify retrySelectRequestTimeout and linktest intervals.

    Note: HsmsGsCommunicator is NOT an instance of SecsCommunicator.

    /* HSMS-GS-Passive open example */
    HsmsGsCommunicatorConfig config = new HsmsGsCommunicatorConfig();
    config.addSessionId(100);
    config.addSessionId(200);
    config.addSessionId(300);
    config.socketAddress(new InetSocketAddress("127.0.0.1", 5000));
    config.isEquip(true);
    config.connectionMode(HsmsConnectionMode.PASSIVE);
    config.rebindIfPassive(5.0F);
    config.notTrySelectRequest();
    config.timeout().t3(45.0F);
    config.timeout().t6( 5.0F);
    config.timeout().t8( 5.0F);
    config.gem().mdln("MDLN-A");
    config.gem().softrev("000001");
    config.gem().clockType(ClockType.A16);
    
    HsmsGsCommunicator passive = HsmsGsCommunicator.newInstance(config);
    passive.open();
    
    /* HSMS-GS-Active open example */
    HsmsGsCommunicatorConfig config = new HsmsGsCommunicatorConfig();
    config.addSessionId(100);
    config.addSessionId(200);
    config.addSessionId(300);
    config.socketAddress(new InetSocketAddress("127.0.0.1", 5000));
    config.isEquip(false);
    config.connectionMode(HsmsConnectionMode.ACTIVE);
    config.retrySelectRequestTimeout(5.0F);
    config.timeout().t3(45.0F);
    config.timeout().t5(10.0F);
    config.timeout().t6( 5.0F);
    config.timeout().t8( 5.0F);
    config.linktest(180.0F);
    config.gem().clockType(ClockType.A16);
    
    HsmsGsCommunicator active = HsmsGsCommunicator.newInstance(config);
    active.open();
  8. Use SML (SECS Message Language) for messaging

    master

    SML allows you to send and receive messages using string representations instead of manual Secs2 object construction. You can create an SmlMessage from a string, a java.io.Reader, a java.nio.file.Path, or by parsing a CharSequence via SmlMessageParser.

    /* Send Primary-Message using SML */
    SmlMessage primarySml = SmlMessage.of("S1F1 W.");
    active.send(primarySml);
    
    /* Send Reply-Message using SML */
    SmlMessage replySml = SmlMessage.of(
        "S1F2             " +
        "<L               " +
        "   <A \"MDLN-A\">" +
        "   <A \"000001\">" +
        ">.               "
    );
    passive.send(primaryMsg, replySml);
  9. Handle received Primary-Messages and send replies

    master

    To handle incoming messages, add a listener using addSecsMessageReceiveListener. Inside the listener, you can parse the Secs2 data using various get methods. Once processed, you can send a reply using the send method that accepts the original primaryMsg.

    /* 1. Add Listener */
    active.addSecsMessageReceiveListener((SecsMessage msg) -> {
        int strm     = msg.getStream();     /* Stream-Number   */
        int func     = msg.getFunction();   /* Function-Number */
        boolean wbit = msg.wbit();          /* W-Bit           */
        Secs2 secs2  = msg.secs2();         /* SECS-II         */
    });
    
    /* 2. Parse SECS-II (Example) */
    // Assuming msg.secs2() contains: <L [3] <B [1] 0x81> <U2 [1] 1001> <A "ON FIRE"> >
    byte   alcd = msg.secs2().getByte(0, 0);
    int    alid = msg.secs2().getInt(1, 0);
    String altx = msg.secs2().getAscii(2);
    
    /* 3. Send Reply-Message */
    Secs2 reply = Secs2.binary((byte)0x0);  /* <B 0x0> */
    active.send(
        primaryMsg, /* Primary-Message */
        5,          /* Stream-Number   */
        2,          /* Function-Number */
        false,      /* W-Bit           */
        reply       /* Reply-SECS-II   */
    );
  10. Create and open a SECS-I (onTcpIp) Communicator

    master

    SECS-I over TCP/IP requires a pair of communicators: a Secs1OnTcpIpCommunicator (client/master type) and a Secs1OnTcpIpReceiverCommunicator (server/bind type).

    Configure them using Secs1OnTcpIpCommunicatorConfig or Secs1OnTcpIpReceiverCommunicatorConfig respectively. Key parameters include socketAddress, deviceId, isMaster, isEquip, and timeout settings (T1-T4).

    /* SECS-I (onTcpIp) open example (Client/Master) */
    Secs1OnTcpIpCommunicatorConfig config = new Secs1OnTcpIpCommunicatorConfig();
    config.socketAddress(new InetSocketAddress("127.0.0.1", 10000));
    config.deviceId(10);
    config.isMaster(true);
    config.isEquip(true);
    config.timeout().t1( 1.0F);
    config.timeout().t2(15.0F);
    config.timeout().t3(45.0F);
    config.timeout().t4(45.0F);
    config.retry(3);
    config.gem().clockType(ClockType.A16);
    
    SecsCommunicator secs1 = Secs1OnTcpIpCommunicator.newInstance(config);
    secs1.open();
    
    /* SECS-I (onTcpIp) Receiver open example (Server/Bind) */
    Secs1OnTcpIpReceiverCommunicatorConfig config = new Secs1OnTcpIpReceiverCommunicatorConfig();
    config.socketAddress(new InetSocketAddress("127.0.0.1", 10000));
    config.deviceId(10);
    config.isMaster(false);
    config.isEquip(false);
    config.timeout().t1( 1.0F);
    config.timeout().t2(15.0F);
    config.timeout().t3(45.0F);
    config.timeout().t4(45.0F);
    config.retry(3);
    config.gem().clockType(ClockType.A16);
    
    SecsCommunicator secs1r = Secs1OnTcpIpReceiverCommunicator.newInstance(config);
    secs1r.open();
  11. Manage Clock and Time via GEM

    master

    The GEM interface provides utilities for synchronizing and handling SECS-II time messages.

    Synchronizing/Getting Clock

    • Request Clock: Send S2F17 to receive a Clock object, which can be converted to a LocalDateTime.
    • Parsing Received Clock: For received S2F31 messages, create a Clock from the Secs2 data and convert to LocalDateTime.

    Sending Time Messages

    • S2F18 (Reply): Use s2f18(primaryMsg, clock) or the convenience method s2f18Now(primaryMsg).
    • S2F31 (Time Acknowledge): Use s2f31(clock) or s2f31Now().

    Note: The TimeFormat (A[12] or A[16]) is configured via AbstractSecsCommunicatorConfig#gem#clockType.

    // Get Clock
    Clock clock = passive.gem().s2f17();
    LocalDateTime ldt = clock.toLocalDateTime();
    
    // Send S2F18
    active.gem().s2f18Now(primaryMsg);
    
    // Send S2F31
    TIACK tiack = active.gem().s2f31Now();
    
    // Parse received S2F31
    Clock recvClock = Clock.from(recvS2F31Msg.secs2());
    LocalDateTime recvLdt = recvClock.toLocalDateTime();