Initial commit
This commit is contained in:
parent
e92aafc6cf
commit
8df563eb20
|
@ -0,0 +1,121 @@
|
|||
/target
|
||||
/Cargo.lock
|
||||
**/target
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/jetbrains
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=jetbrains
|
||||
|
||||
### JetBrains ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### JetBrains Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
||||
.idea/**/sonarlint/
|
||||
|
||||
# SonarQube Plugin
|
||||
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
||||
.idea/**/sonarIssues.xml
|
||||
|
||||
# Markdown Navigator plugin
|
||||
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
||||
.idea/**/markdown-navigator.xml
|
||||
.idea/**/markdown-navigator-enh.xml
|
||||
.idea/**/markdown-navigator/
|
||||
|
||||
# Cache file creation bug
|
||||
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
||||
.idea/$CACHE_FILE$
|
||||
|
||||
# CodeStream plugin
|
||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
||||
.idea/codestream.xml
|
||||
|
||||
# Azure Toolkit for IntelliJ plugin
|
||||
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
|
||||
.idea/**/azureSettings.xml
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/jetbrains
|
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="EMPTY_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/chuniio-tasoller-amdaemon/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/chuniio-tasoller-chusan/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/chuniio-tasoller-common/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/chuniio-tasoller-amdaemon/target" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/chuniio-tasoller-chusan/target" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,6 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="RsStaticConstNaming" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/chuniio-tasoller.iml" filepath="$PROJECT_DIR$/.idea/chuniio-tasoller.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,10 @@
|
|||
[workspace]
|
||||
members = ["chuniio-tasoller-amdaemon", "chuniio-tasoller-chusan", "chuniio-tasoller-common"]
|
||||
resolver = "2"
|
||||
|
||||
[profile.release]
|
||||
strip = true # Automatically strip symbols from the binary.
|
||||
opt-level = "z" # Optimize for size.
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "chuniio-tasoller-amdaemon"
|
||||
version = "0.1.0"
|
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "chuniio-tasoller-amdaemon"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
chuniio-tasoller-common = { path = "../chuniio-tasoller-common" }
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.20"
|
||||
rusb = "0.9.3"
|
||||
winapi = { version = "0.3.9", features = ["ntdef", "winbase", "winerror", "minwindef", "winuser"] }
|
|
@ -0,0 +1,6 @@
|
|||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Configuration {
|
||||
pub test_key: u32,
|
||||
pub service_key: u32,
|
||||
pub coin_key: u32,
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
mod configuration;
|
||||
|
||||
use crate::configuration::Configuration;
|
||||
use chuniio_tasoller_common::create_input_shared_memory;
|
||||
use chuniio_tasoller_common::log::init_logger;
|
||||
use lazy_static::lazy_static;
|
||||
use std::ffi::{c_int, c_void, CString};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU16, Ordering};
|
||||
use winapi::shared::minwindef::{BOOL, DWORD, HINSTANCE, LPVOID, TRUE};
|
||||
use winapi::shared::ntdef::HRESULT;
|
||||
use winapi::shared::winerror::S_OK;
|
||||
use winapi::um::winbase::GetPrivateProfileIntA;
|
||||
use winapi::um::winnt::DLL_PROCESS_ATTACH;
|
||||
use winapi::um::winuser::GetAsyncKeyState;
|
||||
|
||||
static COIN_COUNT: AtomicU16 = AtomicU16::new(0);
|
||||
static COIN_PRESSED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
lazy_static! {
|
||||
static ref CONFIGURATION: Configuration = {
|
||||
let io3 = CString::new("io3").unwrap();
|
||||
let test = CString::new("test").unwrap();
|
||||
let service = CString::new("service").unwrap();
|
||||
let coin = CString::new("coin").unwrap();
|
||||
let cfg_file = CString::new(".\\segatools.ini").unwrap();
|
||||
|
||||
unsafe {
|
||||
Configuration {
|
||||
test_key: GetPrivateProfileIntA(
|
||||
io3.as_ptr(),
|
||||
test.as_ptr(),
|
||||
0x31,
|
||||
cfg_file.as_ptr(),
|
||||
),
|
||||
service_key: GetPrivateProfileIntA(
|
||||
io3.as_ptr(),
|
||||
service.as_ptr(),
|
||||
0x32,
|
||||
cfg_file.as_ptr(),
|
||||
),
|
||||
coin_key: GetPrivateProfileIntA(
|
||||
io3.as_ptr(),
|
||||
coin.as_ptr(),
|
||||
0x33,
|
||||
cfg_file.as_ptr(),
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "system" fn DllMain(_dll_module: HINSTANCE, call_reason: DWORD, _reserved: LPVOID) -> BOOL {
|
||||
match call_reason {
|
||||
DLL_PROCESS_ATTACH => {
|
||||
init_logger();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
TRUE
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_get_api_version() -> u16 {
|
||||
0x0101
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_jvs_init() -> HRESULT {
|
||||
S_OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn chuni_io_jvs_poll(opbtn: *mut u8, beams: *mut u8) {
|
||||
let Ok(input_shmem) = create_input_shared_memory(false) else {
|
||||
return;
|
||||
};
|
||||
let input = input_shmem.as_slice();
|
||||
let bit = input[3];
|
||||
|
||||
if bit & (1 << 6) != 0 {
|
||||
*opbtn |= 0x1;
|
||||
}
|
||||
|
||||
// fn2
|
||||
if bit & (1 << 7) != 0 {
|
||||
*opbtn |= 0x2;
|
||||
}
|
||||
|
||||
for i in 0..6 {
|
||||
if bit & (1 << i) != 0 {
|
||||
*beams |= 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn chuni_io_jvs_read_coin_counter(out: *mut u16) {
|
||||
if out.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
if GetAsyncKeyState(CONFIGURATION.coin_key as c_int) != 0 {
|
||||
let coin_previously_pressed = COIN_PRESSED
|
||||
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |_| Some(true))
|
||||
.unwrap();
|
||||
if !coin_previously_pressed {
|
||||
COIN_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
} else {
|
||||
COIN_PRESSED.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
*out = COIN_COUNT.load(Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// Stubs. These are handled by chusanApp
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_slider_init() -> HRESULT {
|
||||
S_OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_slider_start(_callback: *const c_void) {}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_slider_set_leds(_rgb: *mut u8) {}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_slider_stop() {}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_led_init() -> HRESULT {
|
||||
S_OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_led_set_colors(_board: u8, _data: *const u8) {}
|
|
@ -0,0 +1,2 @@
|
|||
[build]
|
||||
target = "i686-pc-windows-msvc"
|
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "chuniio-tasoller-chusan"
|
||||
version = "0.1.0"
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "chuniio-tasoller-chusan"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
chuniio-tasoller-common = { path = "../chuniio-tasoller-common" }
|
||||
log = "0.4.20"
|
||||
rusb = "0.9.3"
|
||||
winapi = { version = "0.3.9", features = ["ntdef", "winerror", "minwindef"] }
|
|
@ -0,0 +1,218 @@
|
|||
use std::ffi::c_void;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::OnceLock;
|
||||
use std::thread;
|
||||
use std::thread::JoinHandle;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use rusb::{DeviceHandle, GlobalContext};
|
||||
use winapi::{
|
||||
shared::{
|
||||
minwindef::{BOOL, DWORD, HINSTANCE, LPVOID, TRUE},
|
||||
ntdef::HRESULT,
|
||||
winerror::S_OK,
|
||||
},
|
||||
um::winnt::DLL_PROCESS_ATTACH,
|
||||
};
|
||||
|
||||
use chuniio_tasoller_common::{create_input_shared_memory, log::init_logger};
|
||||
use log::{error, info};
|
||||
use winapi::shared::winerror::E_FAIL;
|
||||
|
||||
type SliderCallbackFn = unsafe extern "C" fn(data: *const u8);
|
||||
|
||||
static TASOLLER: OnceLock<DeviceHandle<GlobalContext>> = OnceLock::new();
|
||||
static mut SLIDER_THREAD: OnceLock<JoinHandle<()>> = OnceLock::new();
|
||||
static SLIDER_ACTIVE: AtomicBool = AtomicBool::new(false);
|
||||
static mut USB_OUT: [u8; 240] = [0; 240];
|
||||
|
||||
#[no_mangle]
|
||||
extern "system" fn DllMain(_dll_module: HINSTANCE, call_reason: DWORD, _reserved: LPVOID) -> BOOL {
|
||||
match call_reason {
|
||||
DLL_PROCESS_ATTACH => {
|
||||
init_logger();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
TRUE
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_get_api_version() -> u16 {
|
||||
0x0101
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_slider_init() -> HRESULT {
|
||||
info!("chuni_io_slider_init");
|
||||
|
||||
match tasoller_init() {
|
||||
Ok(_) => S_OK,
|
||||
Err(e) => {
|
||||
error!("Failed to initialize Tasoller: {e:#}");
|
||||
E_FAIL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_slider_start(callback: *const c_void) {
|
||||
if callback.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
if SLIDER_THREAD.get().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
SLIDER_ACTIVE.store(true, Ordering::Relaxed);
|
||||
|
||||
let callback = std::mem::transmute::<_, SliderCallbackFn>(callback);
|
||||
let thread = thread::spawn(move || {
|
||||
let input_shmem = match create_input_shared_memory(false) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
error!("Could not obtain shared memory for Tasoller input: {e:#}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let usb_in = input_shmem.as_slice();
|
||||
let mut pressure = [0u8; 32];
|
||||
|
||||
while SLIDER_ACTIVE.load(Ordering::Relaxed) {
|
||||
for i in 0..32 {
|
||||
pressure[if i % 2 == 0 { 30 - i } else { 32 - i }] = usb_in[i + 4];
|
||||
}
|
||||
|
||||
callback(pressure.as_ptr());
|
||||
thread::sleep(Duration::from_nanos(1_000_000));
|
||||
}
|
||||
});
|
||||
|
||||
SLIDER_THREAD.set(thread).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_slider_set_leds(data: *const u8) {
|
||||
if data.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let Some(device) = TASOLLER.get() else { return };
|
||||
let led_report = std::slice::from_raw_parts(data, 96);
|
||||
|
||||
for n in 0..31 {
|
||||
USB_OUT[n * 3 + 3] = led_report[n * 3 + 2];
|
||||
USB_OUT[n * 3 + 4] = led_report[n * 3 + 1];
|
||||
USB_OUT[n * 3 + 5] = led_report[n * 3 + 0];
|
||||
}
|
||||
|
||||
if let Err(e) = device.write_bulk(0x03, &USB_OUT, Duration::from_micros(1)) {
|
||||
error!("Error writing slider LED data to Tasoller: {e:#}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn chuni_io_slider_stop() {
|
||||
let Some(thread) = SLIDER_THREAD.take() else {
|
||||
return;
|
||||
};
|
||||
|
||||
SLIDER_ACTIVE.store(false, Ordering::Relaxed);
|
||||
|
||||
thread.join().expect("Couldn't join slider input thread");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_led_init() -> HRESULT {
|
||||
S_OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_led_set_colors(board: u8, data: *const u8) {
|
||||
if data.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let start = if board == 0 { 96 } else { 168 };
|
||||
let rgb_start = if board == 0 { 0x96 } else { 0xb4 };
|
||||
|
||||
unsafe {
|
||||
let Some(device) = TASOLLER.get() else { return };
|
||||
|
||||
for n in 0..24 {
|
||||
USB_OUT[start + n * 3 + 1] = *data.wrapping_add(rgb_start);
|
||||
USB_OUT[start + n * 3 + 0] = *data.wrapping_add(rgb_start + 1);
|
||||
USB_OUT[start + n * 3 + 2] = *data.wrapping_add(rgb_start + 2);
|
||||
}
|
||||
|
||||
if let Err(e) = device.write_bulk(0x03, &USB_OUT, Duration::from_micros(1)) {
|
||||
error!("Error writing LED data to Tasoller: {e:#}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tasoller_init() -> Result<()> {
|
||||
let mut device = match rusb::open_device_with_vid_pid(0x1CCF, 0x2333) {
|
||||
Some(dev) => dev,
|
||||
None => {
|
||||
return Err(anyhow!("Tasoller not found."));
|
||||
}
|
||||
};
|
||||
|
||||
device
|
||||
.claim_interface(0)
|
||||
.map_err(|e| anyhow!("Cannot open Tasoller: {e:#}"))?;
|
||||
|
||||
TASOLLER
|
||||
.set(device)
|
||||
.map_err(|e| anyhow!("Cannot store device handle: {e:#?}"))?;
|
||||
|
||||
unsafe {
|
||||
USB_OUT[0] = 0x42;
|
||||
USB_OUT[1] = 0x4C;
|
||||
USB_OUT[2] = 0x00;
|
||||
}
|
||||
|
||||
thread::spawn(input_thread_proc);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn input_thread_proc() {
|
||||
info!("Input thread started");
|
||||
|
||||
let mut shmem = match create_input_shared_memory(true) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
error!("Could not obtain shared memory for Tasoller input: {e:#}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let usb_in = unsafe { shmem.as_slice_mut() };
|
||||
let device = TASOLLER.get().unwrap();
|
||||
|
||||
loop {
|
||||
if let Err(e) = device.read_interrupt(0x84, usb_in, Duration::from_micros(1)) {
|
||||
error!("Failed to read data from Tasoller: {e:#}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stubs. These are handled by the amdaemon dll.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_jvs_init() -> HRESULT {
|
||||
S_OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_jvs_poll(_opbtn: *mut u8, _beams: *mut u8) {}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn chuni_io_jvs_read_coin_counter(_total: *mut u8) {}
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "chuniio-tasoller-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
chrono = "0.4.31"
|
||||
env_logger = "0.10.1"
|
||||
log = "0.4.20"
|
||||
rusb = "0.9.3"
|
||||
shared_memory = "0.12.4"
|
||||
winapi = { version = "0.3.9", features = ["debugapi"] }
|
|
@ -0,0 +1,24 @@
|
|||
pub mod log;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use shared_memory::{Shmem, ShmemConf, ShmemError};
|
||||
|
||||
pub fn create_input_shared_memory(is_owner: bool) -> Result<Shmem> {
|
||||
let shmem_conf = ShmemConf::new().size(36).os_id("tasoller_input");
|
||||
let shmem_result = match shmem_conf.clone().create() {
|
||||
Ok(s) => Ok(s),
|
||||
Err(ShmemError::MappingIdExists) => shmem_conf.open(),
|
||||
Err(e) => {
|
||||
return Err(anyhow!(
|
||||
"Failed to create/open shared memory tasoller_input: {e:#}"
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
shmem_result
|
||||
.map(|mut m| {
|
||||
m.set_owner(is_owner);
|
||||
m
|
||||
})
|
||||
.map_err(|e| anyhow!("Failed to create/open shared memory tasoller_input: {e:#}"))
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
use std::ffi::CString;
|
||||
use std::fmt;
|
||||
use std::io::Write;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use winapi::um::debugapi::OutputDebugStringA;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Logger {}
|
||||
|
||||
impl Write for Logger {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
if let Ok(c_str) = CString::new(buf) {
|
||||
unsafe { OutputDebugStringA(c_str.as_ptr()) }
|
||||
}
|
||||
|
||||
std::io::stdout().write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
std::io::stdout().flush()
|
||||
}
|
||||
}
|
||||
|
||||
struct Padded<T> {
|
||||
pub value: T,
|
||||
pub width: usize,
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for Padded<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{: <width$}", self.value, width = self.width)
|
||||
}
|
||||
}
|
||||
|
||||
static MAX_MODULE_WIDTH: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
fn max_target_width(target: &str) -> usize {
|
||||
let max_width = MAX_MODULE_WIDTH.load(Ordering::Relaxed);
|
||||
if max_width < target.len() {
|
||||
MAX_MODULE_WIDTH.store(target.len(), Ordering::Relaxed);
|
||||
target.len()
|
||||
} else {
|
||||
max_width
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_logger() {
|
||||
env_logger::builder()
|
||||
.filter_level(::log::LevelFilter::Error)
|
||||
.filter_module(
|
||||
"chuniio-tasoller",
|
||||
if cfg!(debug_assertions) {
|
||||
::log::LevelFilter::Debug
|
||||
} else {
|
||||
::log::LevelFilter::Info
|
||||
},
|
||||
)
|
||||
.parse_default_env()
|
||||
.target(env_logger::Target::Pipe(Box::new(Logger {})))
|
||||
.format(|f, record| {
|
||||
let target = record.target();
|
||||
let max_width = max_target_width(target);
|
||||
|
||||
let level = record.level();
|
||||
|
||||
let mut style = f.style();
|
||||
let target = style.set_bold(true).value(Padded {
|
||||
value: target,
|
||||
width: max_width,
|
||||
});
|
||||
|
||||
let time = chrono::Local::now().format("%d/%m/%Y %H:%M:%S");
|
||||
|
||||
writeln!(f, "[{}] {} {} -> {}", time, level, target, record.args())
|
||||
})
|
||||
.init();
|
||||
}
|
Reference in New Issue